From daniel.smith at oracle.com Tue Sep 1 00:16:47 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 31 Aug 2020 18:16:47 -0600 Subject: switch: using an expicit type as total is dangerous In-Reply-To: <1989167974.1130932.1598787455898.JavaMail.zimbra@u-pem.fr> References: <1809503421.343196.1598278106154.JavaMail.zimbra@u-pem.fr> <9a93449a-6ab6-b9ad-c30c-189b5d1b64ad@oracle.com> <1989167974.1130932.1598787455898.JavaMail.zimbra@u-pem.fr> Message-ID: <726B6BB0-6B1A-41A3-9ECE-95CB8087C74E@oracle.com> > On Aug 30, 2020, at 5:37 AM, forax at univ-mlv.fr wrote: > > Ok, let's take an example, i've written a method getLiteral() > Number getLiteral(String token) { > if (token.equals("null")) { > return null; // null is part of the domain > } > try { > return Integer.parseInt(token); > } catch(NumberFormatException e) { > return Double.parseDouble(token); > } > } > > and a statement switch in another package/module > switch(getLiteral(token)) { > case Integer -> System.out.println("Integer"); > case Double -> System.out.println("Double"); > case Number -> System.out.println("null"); > } > > but now i change getLiteral() to add string literal > Object getLiteral(String token) { > if (token.equals("null")) { > return null; // null is part of the domain > } > if (token.startsWith("\"") { > return token.substring(1, token.length() - 1); > } > try { > return Integer.parseInt(token); > } catch(NumberFormatException e) { > return Double.parseDouble(token); > } > } > > If i only recompile getLiteral(), and run the code containing the switch, i get a ICCE at runtime because the signature of getLiteral() has changed, which is good, > but if i now recompile the switch, the code compiles without any error but with a different semantics, duh ? One thing to notice is that, depending on context, the real bug report coming out of this sequence of events is quite likely to have the form "The Widget.poke method doesn't behave correctly when operating on a string." The subtle difference in the treatment of 'null' may be relatively obscure. Anyway, what I think you're really after is a way for the programmer to assert that silently falling out of this switch is unexpected. And 'sealed switch' (however expressed syntactically) is the tool you need to do that. (Let's not dive into aspects of that feature here, though, there's another thread.) Another thing you may appreciate is a warning or error if a non-sealed switch has a total case, because if the switch is total, it's very likely expected to be total forever. We could do this with optimistically-total enum switches, too. I don't think I'd want to do it with 'default' switches, which can be thought of as the "legacy" version of 'sealed switch'. (Whether this is a code-style-enforcing 'lint' warning or a compiler error probably comes down to a cost-benefit analysis. "How likely is the switch operand type to change?" vs. "How annoyed will programmers be that we're making them add 'sealed' or rewrite to 'default'?") From forax at univ-mlv.fr Tue Sep 1 00:37:24 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 1 Sep 2020 02:37:24 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> Message-ID: <1303324486.498541.1598920644018.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Mardi 1 Septembre 2020 01:32:35 > Objet: Re: [pattern-switch] Opting into totality >> Given that if there is a default it's already a sealed switch and that >> i can add a default to make it a sealed switch, >> i struggle to see where to use a classical statement switch and where >> to use a sealed switch ? > > It feels like we're going in circles :) > > One point here is that total switches are generally better _without_ > default clauses, if that is semantically practical (e.g., enums, sealed > types, total type patterns) because then you don't have a catch-all that > sweeps mistakes under the rug.? But the there needs to be a way to > engage totality checking / handling for statements. > > Totalizing with default should not be your first move. In that case, what's the point of allowing default ? And you did not really answer to my question, when should i should prefer one to the other ? R?mi From forax at univ-mlv.fr Tue Sep 1 00:54:46 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 1 Sep 2020 02:54:46 +0200 (CEST) Subject: switch: using an expicit type as total is dangerous In-Reply-To: <726B6BB0-6B1A-41A3-9ECE-95CB8087C74E@oracle.com> References: <1809503421.343196.1598278106154.JavaMail.zimbra@u-pem.fr> <9a93449a-6ab6-b9ad-c30c-189b5d1b64ad@oracle.com> <1989167974.1130932.1598787455898.JavaMail.zimbra@u-pem.fr> <726B6BB0-6B1A-41A3-9ECE-95CB8087C74E@oracle.com> Message-ID: <25304269.498704.1598921686149.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "daniel smith" > ?: "Remi Forax" > Cc: "Brian Goetz" , "amber-spec-experts" > Envoy?: Mardi 1 Septembre 2020 02:16:47 > Objet: Re: switch: using an expicit type as total is dangerous >> On Aug 30, 2020, at 5:37 AM, forax at univ-mlv.fr wrote: >> >> Ok, let's take an example, i've written a method getLiteral() >> Number getLiteral(String token) { >> if (token.equals("null")) { >> return null; // null is part of the domain >> } >> try { >> return Integer.parseInt(token); >> } catch(NumberFormatException e) { >> return Double.parseDouble(token); >> } >> } >> >> and a statement switch in another package/module >> switch(getLiteral(token)) { >> case Integer -> System.out.println("Integer"); >> case Double -> System.out.println("Double"); >> case Number -> System.out.println("null"); >> } >> >> but now i change getLiteral() to add string literal >> Object getLiteral(String token) { >> if (token.equals("null")) { >> return null; // null is part of the domain >> } >> if (token.startsWith("\"") { >> return token.substring(1, token.length() - 1); >> } >> try { >> return Integer.parseInt(token); >> } catch(NumberFormatException e) { >> return Double.parseDouble(token); >> } >> } >> >> If i only recompile getLiteral(), and run the code containing the switch, i get >> a ICCE at runtime because the signature of getLiteral() has changed, which is >> good, >> but if i now recompile the switch, the code compiles without any error but with >> a different semantics, duh ? > > One thing to notice is that, depending on context, the real bug report coming > out of this sequence of events is quite likely to have the form "The > Widget.poke method doesn't behave correctly when operating on a string." The > subtle difference in the treatment of 'null' may be relatively obscure. It is obscure because null is usually not part of the domain so it will take a long time to debug this kind of issue. > > Anyway, what I think you're really after is a way for the programmer to assert > that silently falling out of this switch is unexpected. And 'sealed switch' > (however expressed syntactically) is the tool you need to do that. (Let's not > dive into aspects of that feature here, though, there's another thread.) yes, it's one solution, the other is to always use 'var' when what you want is a total pattern. > > Another thing you may appreciate is a warning or error if a non-sealed switch > has a total case, because if the switch is total, it's very likely expected to > be total forever. We could do this with optimistically-total enum switches, > too. I don't think I'd want to do it with 'default' switches, which can be > thought of as the "legacy" version of 'sealed switch'. It's more or less what a part of what i've proposed in another thread, i propose to emit a warning even if there is a default. Combined with the fact that you can declare a total switch means that you can suggest to users to retrofit the existing enum switch (and uses of the new switch on types) to a total switch. > > (Whether this is a code-style-enforcing 'lint' warning or a compiler error > probably comes down to a cost-benefit analysis. "How likely is the switch > operand type to change?" vs. "How annoyed will programmers be that we're making > them add 'sealed' or rewrite to 'default'?") If IDEs provide this an automatic refactoring, the cost of changing the code may be greatly reduced. R?mi From forax at univ-mlv.fr Tue Sep 1 01:11:10 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 1 Sep 2020 03:11:10 +0200 (CEST) Subject: [pattern-switch] Exhaustiveness In-Reply-To: References: <50A45261-109C-4F73-9109-E3174E57D325@oracle.com> <2927f36f-3231-14c4-f8d4-f05685efd6e1@oracle.com> <41E50468-648B-4A29-8AB6-5AB0871ED320@oracle.com> <0c73fc74-d965-48ea-68fe-80f480b7e464@oracle.com> Message-ID: <877299929.499833.1598922670040.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "daniel smith" > Cc: "Guy Steele" , "Remi Forax" , "Tagir Valeev" , > "amber-spec-experts" > Envoy?: Mardi 1 Septembre 2020 01:30:15 > Objet: Re: [pattern-switch] Exhaustiveness >> I'm still thinking it's worthwhile to change the behavior of enum switches to >> throw the same thing as sealed class switches. > > I don't necessarily think this is terrible, but see inline. > >> >> - ICCE is arguably just wrong. It's not an incompatible change to add an enum >> constant to an enum declaration. > > Kevin made a compelling argument when we did this that ICCE is actually > right.? The notion is that ICCE is what users are most likely to > associate with "broken classpath"; that the classes on the class path > have been inconsistently compiled.? ICCEs go away on recompilation. > > This does, of course, hinge on the meaning of incompatible.? Adding an > enum constant is not binary-incompatible; all the call sites continue to > link.? But "I've never seen this value before", because the value wasn't > present at compilation time, is not an unreasonable interpretation. > >> - The inconsistency is a risk?somebody thinks they're catching the exception, >> then they discover, oops, enum switches throw a different exception type for >> historical reasons. yes, replacing some enum values by a "case var enumValue ->" is enough to change the semantics. > > Yeah, don't care much about this.? No one catches these anyway. Given all mocking libraries that exist, the number of people under the "no one" category may be bigger than you estimate. Also there is a lot of code that doesn't do an explicit null check on top of a public method because the first instruction is a switch, and i'm sure some of them are covered by a unit test. > >> Of course, behavioral changes are a risk, too, but I think getting it in early, >> before there's been much time for adoption of switch expressions and evolution >> of enums, minimizes that risk. (We could even make the change in 16 as a spec >> bug fix.) If we decide to use an UnexpectedFrogException, i will vote for a bug fix. > > I don't necessarily disagree, and it would be consistent to throw > UnexpectedFrogException for all non-null remainders, but the status quo > does not seem wrong to me. R?mi From daniel.smith at oracle.com Tue Sep 1 01:56:21 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 31 Aug 2020 19:56:21 -0600 Subject: [pattern-switch] Opting into totality In-Reply-To: <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> Message-ID: > On Aug 31, 2020, at 5:04 PM, Remi Forax wrote: > > Given that if there is a default it's already a sealed switch and that i can add a default to make it a sealed switch, > i struggle to see where to use a classical statement switch and where to use a sealed switch ? A competent programmer will definitely need to be able to answer the question "should I add 'sealed' to this switch?" I'll take a stab at enumerating all the cases: - A switch statement with a 'default' case: doesn't matter. The language already supports 'default' in both forms (existing switch statements are non-sealed, existing switch expressions are sealed). Pick a favorite style. (Alternatively: a switch statement with a 'default' clause is implicitly sealed. Then it's a style question of whether or not to be explicit about it.) - A switch statement that initializes a local variable or returns at the end of a method: doesn't matter. If you don't say 'sealed', flow analysis will catch any mistakes. (But there are some gotchas, so maybe 'sealed' (or switch expression) is the way to go if you don't want to think about it.) - A switch statement that side-effects on just a few of the possible inputs ("possible" per static types): must use a non-sealed switch. - A switch statement that is optimistically total over an enum/sealed class: use a sealed switch to ensure totality checking in the future. Or, if the totality is accidental (I cover the cases right now, but don't expect to in the future), use a non-sealed switch. - A switch statement with a last 'case' intended to be total: use a sealed switch to avoid mistakes and (if it's a risk) defend against input type changes I think that covers it? There will be some coding style preferences to work out, but I think this story will be intuitive to most programmers. From forax at univ-mlv.fr Tue Sep 1 12:08:33 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 1 Sep 2020 14:08:33 +0200 (CEST) Subject: [pattern-switch] Exhaustiveness In-Reply-To: <877299929.499833.1598922670040.JavaMail.zimbra@u-pem.fr> References: <2927f36f-3231-14c4-f8d4-f05685efd6e1@oracle.com> <41E50468-648B-4A29-8AB6-5AB0871ED320@oracle.com> <0c73fc74-d965-48ea-68fe-80f480b7e464@oracle.com> <877299929.499833.1598922670040.JavaMail.zimbra@u-pem.fr> Message-ID: <1791727075.772114.1598962113324.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Remi Forax" > ?: "Brian Goetz" > Cc: "daniel smith" , "Guy Steele" , "Tagir Valeev" , > "amber-spec-experts" > Envoy?: Mardi 1 Septembre 2020 03:11:10 > Objet: Re: [pattern-switch] Exhaustiveness > ----- Mail original ----- >> De: "Brian Goetz" >> ?: "daniel smith" >> Cc: "Guy Steele" , "Remi Forax" , >> "Tagir Valeev" , >> "amber-spec-experts" >> Envoy?: Mardi 1 Septembre 2020 01:30:15 >> Objet: Re: [pattern-switch] Exhaustiveness > >>> I'm still thinking it's worthwhile to change the behavior of enum switches to >>> throw the same thing as sealed class switches. >> >> I don't necessarily think this is terrible, but see inline. >> >>> >>> - ICCE is arguably just wrong. It's not an incompatible change to add an enum >>> constant to an enum declaration. >> >> Kevin made a compelling argument when we did this that ICCE is actually >> right.? The notion is that ICCE is what users are most likely to >> associate with "broken classpath"; that the classes on the class path >> have been inconsistently compiled.? ICCEs go away on recompilation. >> >> This does, of course, hinge on the meaning of incompatible.? Adding an >> enum constant is not binary-incompatible; all the call sites continue to >> link.? But "I've never seen this value before", because the value wasn't >> present at compilation time, is not an unreasonable interpretation. >> >>> - The inconsistency is a risk?somebody thinks they're catching the exception, >>> then they discover, oops, enum switches throw a different exception type for >>> historical reasons. > > yes, replacing some enum values by a "case var enumValue ->" is enough to change > the semantics. just to be clear here is an example switch Color { red, green, blue, pink } switch(color) { case "pink" -> System.out.println("contains red"); case "blue" -> System.out.println("do not contains r"); case Color c where c != null -> System.out.println("contains r"); } this switch is optimistically total but if color is null it will not throw a NPE. > >> >> Yeah, don't care much about this.? No one catches these anyway. > > Given all mocking libraries that exist, the number of people under the "no one" > category may be bigger than you estimate. > > Also there is a lot of code that doesn't do an explicit null check on top of a > public method because the first instruction is a switch, and i'm sure some of > them are covered by a unit test. > >> >>> Of course, behavioral changes are a risk, too, but I think getting it in early, >>> before there's been much time for adoption of switch expressions and evolution >>> of enums, minimizes that risk. (We could even make the change in 16 as a spec >>> bug fix.) > > If we decide to use an UnexpectedFrogException, i will vote for a bug fix. > >> >> I don't necessarily disagree, and it would be consistent to throw >> UnexpectedFrogException for all non-null remainders, but the status quo >> does not seem wrong to me. > > R?mi R?mi From forax at univ-mlv.fr Tue Sep 1 12:36:51 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 1 Sep 2020 14:36:51 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> Message-ID: <1360493368.802601.1598963811929.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Lundi 31 Ao?t 2020 15:25:13 > Objet: Re: [pattern-switch] Opting into totality > I think this is the main open question at this point. > We now have a deeper understanding of what this means, and the shape of the > remainder. Totality means not only ?spot check me that I?m right?, but also ?I > know there might be some remainder, please deal with it.? So totality is not > merely about type checking, but about affirmative handling of the remainder. > Expression switches automatically get this treatment, and opting _out_ of that > makes no sense for expression switches (expressions must be total), but > statement switches make sense both ways (just like unbalanced and balanced > if-else.) Unfortunately the default has to be partial, so the main question is, > how do we indicate the desire for totality in a way that is properly evocative > for the user? > We?ve talked about modifying switch (sealed switch), a hyphenated keyword > (total-switch), a trailing modifier (switch case), and synthetic cases > (?default: unreachable?). Of course at this point it?s ?just syntax?, but I > think our goal should be picking something that makes it obvious to users that > what?s going on is not merely an assertion of totality, but also a desire to > handle the remainder. >> - How does a switch opt into totality, other than by being an expression switch? With the current spec knowing if a switch is total or not depend the last item (it's not fully true) of the switch, if the last item is default, case var, etc so it seems weird to me to have to opt-in upfront instead of at the end of the switch. what about something like switch(foo) { case ... case ... check-that-this-switch-is-optimistically-total-please; } obviously, there is perhaps a better keyword than check-that-this-swicth-is-optimistically-total-please, something like implicit-default (using default as a hyphen part is a subconscious message to the fact that using a default here is not expected) or sealed. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Sep 1 12:44:35 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 1 Sep 2020 14:44:35 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> Message-ID: <783971148.809114.1598964275023.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "daniel smith" > ?: "Remi Forax" > Cc: "Brian Goetz" , "amber-spec-experts" > Envoy?: Mardi 1 Septembre 2020 03:56:21 > Objet: Re: [pattern-switch] Opting into totality >> On Aug 31, 2020, at 5:04 PM, Remi Forax wrote: >> >> Given that if there is a default it's already a sealed switch and that i can add >> a default to make it a sealed switch, >> i struggle to see where to use a classical statement switch and where to use a >> sealed switch ? > > A competent programmer will definitely need to be able to answer the question > "should I add 'sealed' to this switch?" I'll take a stab at enumerating all the > cases: > > - A switch statement with a 'default' case: doesn't matter. The language already > supports 'default' in both forms (existing switch statements are non-sealed, > existing switch expressions are sealed). Pick a favorite style. (Alternatively: > a switch statement with a 'default' clause is implicitly sealed. Then it's a > style question of whether or not to be explicit about it.) > > - A switch statement that initializes a local variable or returns at the end of > a method: doesn't matter. If you don't say 'sealed', flow analysis will catch > any mistakes. (But there are some gotchas, so maybe 'sealed' (or switch > expression) is the way to go if you don't want to think about it.) > > - A switch statement that side-effects on just a few of the possible inputs > ("possible" per static types): must use a non-sealed switch. > > - A switch statement that is optimistically total over an enum/sealed class: use > a sealed switch to ensure totality checking in the future. Or, if the totality > is accidental (I cover the cases right now, but don't expect to in the future), > use a non-sealed switch. > > - A switch statement with a last 'case' intended to be total: use a sealed > switch to avoid mistakes and (if it's a risk) defend against input type changes > > I think that covers it? There will be some coding style preferences to work out, > but I think this story will be intuitive to most programmers. Thanks, in another thread, i argue that using a keyword at the place of 'default' is perhaps a better syntax, because if it's not a upfront keyword, so as a developer i will not have to decide which kind of switch to use until i'm at the end of the switch and at that point i can think in term of totality. The last three items of your list seems to confirm that idea. R?mi From brian.goetz at oracle.com Tue Sep 1 14:09:52 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Sep 2020 10:09:52 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <55264041-ACFD-446D-A6DF-CDE9892C157C@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <55264041-ACFD-446D-A6DF-CDE9892C157C@oracle.com> Message-ID: <4e385ec2-0d00-d3b2-b906-4cdf18cbed3c@oracle.com> > It's unclear whether your "some remainder" is allowed to be empty. (There was some discussion earlier about outlawing 'default' in the equivalent of a sealed switch.) I hope full totality is fine?an expression switch, implicitly 'sealed', of course permits a 'default' clause. Yes, remainder can be empty.? The key aspect of sealed switches is that we generate code to handle the remainder.? Of course, it could be handled explicitly too, in which case the synthetic handler is never matched.? For example: ??? sealed switch (foo ) { ??????? case Foo(var x): ... ??? } is total on Foo with remainder { null }.? So sealing will cause us to throw on null.? OTOH, you could also say: ??? sealed switch (foo ) { ??????? case null: haha(); break; ??????? case Foo(var x): ... ??? } The compiler does not need to analyze the cases that might match remainder; it just generates code at the end of the switch so that, if a previous case doesn't cover it, the catch-all does. > And then note that, given the existence of 'sealed switch', the 'default Object o' feature is redundant. If you want to make sure you have a total case in your switch, just say 'sealed' at the top. All sealed switches (both statement and expression) guarantee either optimistic totality + NPE or that the last clause is total. Yes, that's right.?? If there's a way to ask for totality, you can safely use a total pattern without `default`.? So `default`, in this model, reverts to being `case _`, which is a fine role for default in this new world. From brian.goetz at oracle.com Tue Sep 1 14:13:00 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Sep 2020 10:13:00 -0400 Subject: switch: using an expicit type as total is dangerous In-Reply-To: <726B6BB0-6B1A-41A3-9ECE-95CB8087C74E@oracle.com> References: <1809503421.343196.1598278106154.JavaMail.zimbra@u-pem.fr> <9a93449a-6ab6-b9ad-c30c-189b5d1b64ad@oracle.com> <1989167974.1130932.1598787455898.JavaMail.zimbra@u-pem.fr> <726B6BB0-6B1A-41A3-9ECE-95CB8087C74E@oracle.com> Message-ID: > Anyway, what I think you're really after is a way for the programmer to assert that silently falling out of this switch is unexpected. And 'sealed switch' (however expressed syntactically) is the tool you need to do that. (Let's not dive into aspects of that feature here, though, there's another thread.) Yes, this is the key thing that is missing.? The rest is mostly a distraction. > Another thing you may appreciate is a warning or error if a non-sealed switch has a total case, because if the switch is total, it's very likely expected to be total forever. We could do this with optimistically-total enum switches, too. I don't think I'd want to do it with 'default' switches, which can be thought of as the "legacy" version of 'sealed switch'. I'd leave this to IDEs to suggest.? It's a fine suggestion -- "I notice you are coding a total switch, let's buy some insurance on it staying that way." From brian.goetz at oracle.com Tue Sep 1 14:22:26 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Sep 2020 10:22:26 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> Message-ID: On 8/31/2020 9:56 PM, Dan Smith wrote: > > A competent programmer will definitely need to be able to answer the question "should I add 'sealed' to this switch?" I'll take a stab at enumerating all the cases: > > - A switch statement with a 'default' case: doesn't matter. The language already supports 'default' in both forms (existing switch statements are non-sealed, existing switch expressions are sealed). Pick a favorite style. (Alternatively: a switch statement with a 'default' clause is implicitly sealed. Then it's a style question of whether or not to be explicit about it.) > > - A switch statement that initializes a local variable or returns at the end of a method: doesn't matter. If you don't say 'sealed', flow analysis will catch any mistakes. (But there are some gotchas, so maybe 'sealed' (or switch expression) is the way to go if you don't want to think about it.) Yeah, while in many cases flow analysis will save us, I'm not sure this is the message we want to send; it is not uncommon when presented with a flow error ("variable x might not be initialized) to "fix" it with "int x = 0".? (Gotcha, stupid compiler.)? I think these cases are prime examples of "falling out of this switch silently is a mistake", so I would advocate for sealing in all these cases, rather than hoping they used flow analysis correctly. > - A switch statement that side-effects on just a few of the possible inputs ("possible" per static types): must use a non-sealed switch. Not "must", since you can have a default that does nothing in a sealed switch. But, there is a subtle difference between ??? switch (x) { ??????? case FOO: ... ??? } and ??? sealed switch (x) { ??????? case FOO: .... ??????? default: // nothing ??? } which is, what happens on remainder.? In the former, it is just another ignored non-matching input; in the latter, we throw. I believe this is the subtle difference that will get people. > - A switch statement that is optimistically total over an enum/sealed class: use a sealed switch to ensure totality checking in the future. Or, if the totality is accidental (I cover the cases right now, but don't expect to in the future), use a non-sealed switch. What we were calling optimistic totality (now, totality with remainder) is not just about enums and sealed classes, though. Consider: ??? switch (foo) { ??????? case Foo(var x): ... ??? } There is a remainder, null, and a sealed switch will NPE on it.? (As will a pattern assignment.) > - A switch statement with a last 'case' intended to be total: use a sealed switch to avoid mistakes and (if it's a risk) defend against input type changes > > I think that covers it? There will be some coding style preferences to work out, but I think this story will be intuitive to most programmers. > Yep.? I think the key difficult think here, which took us a while to see, is that the remainder shape can get complicated, especially when you get to nested deconstruction patterns over sealed types. We agreed that this is unavoidable, and hopefully will pop into the user's consciousness rarely. The key difference between a sealed switch and a non-sealed one is that in a sealed switch, there is _no_ input value which is silently ignored. From daniel.smith at oracle.com Tue Sep 1 17:27:10 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 1 Sep 2020 11:27:10 -0600 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> Message-ID: <24833539-E8E6-4B43-A2AF-52F9BABB92FD@oracle.com> > On Sep 1, 2020, at 8:22 AM, Brian Goetz wrote: > > But, there is a subtle difference between > > switch (x) { > case FOO: ... > } > > and > > sealed switch (x) { > case FOO: .... > default: // nothing > } > > which is, what happens on remainder. In the former, it is just another ignored non-matching input; in the latter, we throw. Confused here. Doesn't 'default' handle the remainder explicitly? Under what conditions does your sealed switch throw? The way I'm modeling remainder handling in my head is that sealed switches without 'default' get an implicit 'default' clause that throws. (And, for that matter, regular switches without 'default' get an implicit 'default' that is a no-op.) From brian.goetz at oracle.com Tue Sep 1 17:43:18 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Sep 2020 13:43:18 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <24833539-E8E6-4B43-A2AF-52F9BABB92FD@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <8660C84F-338F-4F08-A5B5-CA0BBA99F970@oracle.com> <355666053.496907.1598915071108.JavaMail.zimbra@u-pem.fr> <24833539-E8E6-4B43-A2AF-52F9BABB92FD@oracle.com> Message-ID: <5e0cce4a-6c37-9ddc-6f0d-82adf6901381@oracle.com> Sorry, I got my examples mixed up.? I'll try to reconstruct what I was saying. On 9/1/2020 1:27 PM, Dan Smith wrote: >> On Sep 1, 2020, at 8:22 AM, Brian Goetz wrote: >> >> But, there is a subtle difference between >> >> switch (x) { >> case FOO: ... >> } >> >> and >> >> sealed switch (x) { >> case FOO: .... >> default: // nothing >> } >> >> which is, what happens on remainder. In the former, it is just another ignored non-matching input; in the latter, we throw. > Confused here. Doesn't 'default' handle the remainder explicitly? Under what conditions does your sealed switch throw? > > The way I'm modeling remainder handling in my head is that sealed switches without 'default' get an implicit 'default' clause that throws. (And, for that matter, regular switches without 'default' get an implicit 'default' that is a no-op.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Sep 3 17:04:32 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 3 Sep 2020 19:04:32 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> Message-ID: <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> I just want to say that the is yet another option, say that (statement and expression) arrow switches are always total. We have introduced the arrow notation to avoid fallthrough but we have forgotten one important case of fallthrough, in a statement switch when you skip the entire switch, you fallthrough the entire switch. So we keep supporting the traditional partial switch with no modification but requires if a user wants a partial arrow switch, to add a "default -> {}". This is an incompatible change with the codes written since Java 14 so it's a limited incompatible change. Perhaps the main blocker is admitting that we were wrong. R?mi > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Lundi 31 Ao?t 2020 15:25:13 > Objet: Re: [pattern-switch] Opting into totality > I think this is the main open question at this point. > We now have a deeper understanding of what this means, and the shape of the > remainder. Totality means not only ?spot check me that I?m right?, but also ?I > know there might be some remainder, please deal with it.? So totality is not > merely about type checking, but about affirmative handling of the remainder. > Expression switches automatically get this treatment, and opting _out_ of that > makes no sense for expression switches (expressions must be total), but > statement switches make sense both ways (just like unbalanced and balanced > if-else.) Unfortunately the default has to be partial, so the main question is, > how do we indicate the desire for totality in a way that is properly evocative > for the user? > We?ve talked about modifying switch (sealed switch), a hyphenated keyword > (total-switch), a trailing modifier (switch case), and synthetic cases > (?default: unreachable?). Of course at this point it?s ?just syntax?, but I > think our goal should be picking something that makes it obvious to users that > what?s going on is not merely an assertion of totality, but also a desire to > handle the remainder. >> - How does a switch opt into totality, other than by being an expression switch? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Sep 3 18:16:10 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 3 Sep 2020 14:16:10 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> Message-ID: That came up in the expression switch exploration.? The thinking then, which I think is still valid, that it is easier to understand the difference when default-totality is attached to the expression versions, because expressions _must_ be total and statements totally make sense to be partial. I think this is still coming from a place of retrospective snitch-envy; you want to carve out a corner that has the "right" semantics, even if its relation to the other corners is totally ad-hoc and random.? The upgrade to switch was driven by orthogonality; totality derives from whether the context of the switch (statement vs expression) requires totality or embraces partiality.? And the kinds of labels are strictly about the treatment of what is on the RHS -- either a single { expression/statement } vs complex control flow.? Which is orthogonal to expression/statement. So, I think we got it right then; we just have some holes to patch. On 9/3/2020 1:04 PM, Remi Forax wrote: > I just want to say that the is yet another option, > say that (statement and expression) arrow switches are always total. > > We have introduced the arrow notation to avoid fallthrough but we have > forgotten one important case of fallthrough, in a statement switch > when you skip the entire switch, you fallthrough the entire switch. > > So we keep supporting the traditional partial switch with no > modification but requires if a user wants a partial arrow switch, to > add a "default -> {}". > > This is an incompatible change with the codes written since Java 14 so > it's a limited incompatible change. > Perhaps the main blocker is admitting that we were wrong. > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"amber-spec-experts" > *Envoy?: *Lundi 31 Ao?t 2020 15:25:13 > *Objet: *Re: [pattern-switch] Opting into totality > > I think this is the main open question at this point. > > We now have a deeper understanding of what this means, and the > shape of the remainder. ?Totality means not only ?spot check me > that I?m right?, but also ?I know there might be some remainder, > please deal with it.? ? So totality is not merely about type > checking, but about affirmative handling of the remainder. > > Expression switches automatically get ?this treatment, and opting > _out_ of that makes no sense for expression switches (expressions > must be total), but statement switches make sense both ways (just > like unbalanced and balanced if-else.) ?Unfortunately the default > has to be partial, ?so the main question is, how ?do we indicate > the desire for totality in a way that is properly evocative for > the user? > > We?ve talked about modifying switch (sealed switch), a hyphenated > keyword (total-switch), a trailing modifier (switch case), and > synthetic cases (?default: unreachable?). ?Of course at this point > it?s ?just syntax?, but I think our goal should be picking > something that ?makes it obvious to users that what?s going on is > not merely an assertion of totality, but also a desire to handle > the remainder. > > ? ?- How does a switch opt into totality, other than by being > an expression switch? > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From guy.steele at oracle.com Thu Sep 3 19:06:53 2020 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 3 Sep 2020 15:06:53 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> Message-ID: <78558FA3-8C89-4022-AA9B-621C59930152@oracle.com> > On Sep 3, 2020, at 2:16 PM, Brian Goetz wrote: > > That came up in the expression switch exploration. The thinking then, which I think is still valid, that it is easier to understand the difference when default-totality is attached to the expression versions, because expressions _must_ be total and statements totally make sense to be partial. Which, many theorists would say, is an indictment of statements (both generally and in this specific instance). -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Sep 3 19:13:56 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 3 Sep 2020 15:13:56 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <78558FA3-8C89-4022-AA9B-621C59930152@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <78558FA3-8C89-4022-AA9B-621C59930152@oracle.com> Message-ID: <15b7f589-5a07-32d9-3546-7a931826827c@oracle.com> Indeed, after serialization, statements probably go next on the list of "gifts that keep on giving."? Languages that avoided this mistake have a leg up on languages that didn't. > >> On Sep 3, 2020, at 2:16 PM, Brian Goetz > > wrote: >> >> That came up in the expression switch exploration.? The thinking >> then, which I think is still valid, that it is easier to understand >> the difference when default-totality is attached to the expression >> versions, because expressions _must_ be total and statements totally >> make sense to be partial. > > Which, many theorists would say, is an indictment of statements (both > generally and in this specific instance). > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Sep 3 19:30:08 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 3 Sep 2020 21:30:08 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> Message-ID: <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 3 Septembre 2020 20:16:10 > Objet: Re: [pattern-switch] Opting into totality > That came up in the expression switch exploration. The thinking then, which I > think is still valid, that it is easier to understand the difference when > default-totality is attached to the expression versions, because expressions > _must_ be total and statements totally make sense to be partial. yes, that made sense at the time, the problem is that now we want seom statement switches to be total. > I think this is still coming from a place of retrospective snitch-envy; you want > to carve out a corner that has the "right" semantics, even if its relation to > the other corners is totally ad-hoc and random. It's not random, arrow switch avoid fallthrough in between cases but weirdly allow a to fallthrough the whole switch. So it's not about having the semantics right, it's about making the semantics consistent, here the story being able to fall to the next instruction is not consistent. > The upgrade to switch was driven by orthogonality; totality derives from whether > the context of the switch (statement vs expression) requires totality or > embraces partiality. Very true, but now you are saying that you want to introduce an opt-in to totality for the switch statement, so it's not orthogonal anymore. > And the kinds of labels are strictly about the treatment of what is on the RHS > -- either a single { expression/statement } vs complex control flow. Which is > orthogonal to expression/statement. The fact that you can implicitly skip the whole switch with arrows it's a complex control flow, , so that part is not fully orthogonal too. > So, I think we got it right then; we just have some holes to patch. The problem is that introducing a switch statement that requires totality goes full throttle against the idea that a statement switch implies partiallity. R?mi > On 9/3/2020 1:04 PM, Remi Forax wrote: >> I just want to say that the is yet another option, >> say that (statement and expression) arrow switches are always total. >> We have introduced the arrow notation to avoid fallthrough but we have forgotten >> one important case of fallthrough, in a statement switch when you skip the >> entire switch, you fallthrough the entire switch. >> So we keep supporting the traditional partial switch with no modification but >> requires if a user wants a partial arrow switch, to add a "default -> {}". >> This is an incompatible change with the codes written since Java 14 so it's a >> limited incompatible change. >> Perhaps the main blocker is admitting that we were wrong. >> R?mi >>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>> ?: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>> ] >>> Envoy?: Lundi 31 Ao?t 2020 15:25:13 >>> Objet: Re: [pattern-switch] Opting into totality >>> I think this is the main open question at this point. >>> We now have a deeper understanding of what this means, and the shape of the >>> remainder. Totality means not only ?spot check me that I?m right?, but also ?I >>> know there might be some remainder, please deal with it.? So totality is not >>> merely about type checking, but about affirmative handling of the remainder. >>> Expression switches automatically get this treatment, and opting _out_ of that >>> makes no sense for expression switches (expressions must be total), but >>> statement switches make sense both ways (just like unbalanced and balanced >>> if-else.) Unfortunately the default has to be partial, so the main question is, >>> how do we indicate the desire for totality in a way that is properly evocative >>> for the user? >>> We?ve talked about modifying switch (sealed switch), a hyphenated keyword >>> (total-switch), a trailing modifier (switch case), and synthetic cases >>> (?default: unreachable?). Of course at this point it?s ?just syntax?, but I >>> think our goal should be picking something that makes it obvious to users that >>> what?s going on is not merely an assertion of totality, but also a desire to >>> handle the remainder. >>>> - How does a switch opt into totality, other than by being an expression switch? -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Sep 3 19:33:07 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 3 Sep 2020 21:33:07 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <15b7f589-5a07-32d9-3546-7a931826827c@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <78558FA3-8C89-4022-AA9B-621C59930152@oracle.com> <15b7f589-5a07-32d9-3546-7a931826827c@oracle.com> Message-ID: <331477711.670532.1599161587373.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Guy Steele" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Jeudi 3 Septembre 2020 21:13:56 > Objet: Re: [pattern-switch] Opting into totality > Indeed, after serialization, statements probably go next on the list of "gifts > that keep on giving." Languages that avoided this mistake have a leg up on > languages that didn't. The idea of checked exceptions is worst, you can not compose methods because of that. R?mi >>> On Sep 3, 2020, at 2:16 PM, Brian Goetz < [ mailto:brian.goetz at oracle.com | >>> brian.goetz at oracle.com ] > wrote: >>> That came up in the expression switch exploration. The thinking then, which I >>> think is still valid, that it is easier to understand the difference when >>> default-totality is attached to the expression versions, because expressions >>> _must_ be total and statements totally make sense to be partial. >> Which, many theorists would say, is an indictment of statements (both generally >> and in this specific instance). -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Sep 3 19:37:56 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 3 Sep 2020 15:37:56 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> Message-ID: We knew at the time we were might want to come back for totality of switches -- not as "fixing a mistake", but providing more type checking when the user asks for it.? So I don't think any of the "but now we realize it is different" applies, nor do I see any new evidence that somehow we missed something substantial then.? (I recall you wanted to do a more aggressive "fix mistakes of the past" option at the time; I think this is mostly taking a second swing along those lines.) Note that we still have the alternative to do nothing: let the user be on their own with totality for statement switches.? That wasn't a terrible option in 12 (which is why we pushed it off until now), and it's still not really terrible now; it's just that the number of cases of nontrivial totality, and the complexity of the remainder, has gone up and will go up more in the future (enums yesterday; sealed types today; deconstruction patterns tomorrow), which provides some additional pressure for a "total statement switch" option.? Which we could do now, or later, or never. On 9/3/2020 3:30 PM, forax at univ-mlv.fr wrote: > > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"Remi Forax" > *Cc: *"amber-spec-experts" > *Envoy?: *Jeudi 3 Septembre 2020 20:16:10 > *Objet: *Re: [pattern-switch] Opting into totality > > That came up in the expression switch exploration.? The thinking > then, which I think is still valid, that it is easier to > understand the difference when default-totality is attached to the > expression versions, because expressions _must_ be total and > statements totally make sense to be partial. > > > yes, that made sense at the time, the problem is that now we want seom > statement switches to be total. > > > > I think this is still coming from a place of retrospective > snitch-envy; you want to carve out a corner that has the "right" > semantics, even if its relation to the other corners is totally > ad-hoc and random. > > > It's not random, arrow switch avoid fallthrough in between cases but > weirdly allow a to fallthrough the whole switch. > So it's not about having the semantics right, it's about making the > semantics consistent, here the story being able to fall to the next > instruction is not consistent. > > The upgrade to switch was driven by orthogonality; totality > derives from whether the context of the switch (statement vs > expression) requires totality or embraces partiality. > > > Very true, but now you are saying that you want to introduce an opt-in > to totality for the switch statement, so it's not orthogonal anymore. > > ? And the kinds of labels are strictly about the treatment of what > is on the RHS -- either a single { expression/statement } vs > complex control flow.? Which is orthogonal to expression/statement. > > > The fact that you can implicitly skip the whole switch with arrows > it's a complex control flow, , so that part is not fully orthogonal too. > > > So, I think we got it right then; we just have some holes to patch. > > > The problem is that introducing a switch statement that requires > totality goes full throttle against the idea that a statement switch > implies partiallity. > > R?mi > > > > On 9/3/2020 1:04 PM, Remi Forax wrote: > > I just want to say that the is yet another option, > say that (statement and expression) arrow switches are always > total. > > We have introduced the arrow notation to avoid fallthrough but > we have forgotten one important case of fallthrough, in a > statement switch when you skip the entire switch, you > fallthrough the entire switch. > > So we keep supporting the traditional partial switch with no > modification but requires if a user wants a partial arrow > switch, to add a "default -> {}". > > This is an incompatible change with the codes written since > Java 14 so it's a limited incompatible change. > Perhaps the main blocker is admitting that we were wrong. > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"amber-spec-experts" > > *Envoy?: *Lundi 31 Ao?t 2020 15:25:13 > *Objet: *Re: [pattern-switch] Opting into totality > > I think this is the main open question at this point. > > We now have a deeper understanding of what this means, and > the shape of the remainder. ?Totality means not only ?spot > check me that I?m right?, but also ?I know there might be > some remainder, please deal with it.? ? So totality is not > merely about type checking, but about affirmative handling > of the remainder. > > Expression switches automatically get ?this treatment, and > opting _out_ of that makes no sense for expression > switches (expressions must be total), but statement > switches make sense both ways (just like unbalanced and > balanced if-else.) ?Unfortunately the default has to be > partial, ?so the main question is, how ?do we indicate the > desire for totality in a way that is properly evocative > for the user? > > We?ve talked about modifying switch (sealed switch), a > hyphenated keyword (total-switch), a trailing modifier > (switch case), and synthetic cases (?default: > unreachable?). ?Of course at this point it?s ?just > syntax?, but I think our goal should be picking something > that ?makes it obvious to users that what?s going on is > not merely an assertion of totality, but also a desire to > handle the remainder. > > ?- How does a switch opt into totality, other than by > being an expression switch? > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Sep 3 19:54:45 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 3 Sep 2020 21:54:45 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> Message-ID: <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 3 Septembre 2020 21:37:56 > Objet: Re: [pattern-switch] Opting into totality > We knew at the time we were might want to come back for totality of switches -- > not as "fixing a mistake", but providing more type checking when the user asks > for it. So I don't think any of the "but now we realize it is different" > applies, nor do I see any new evidence that somehow we missed something > substantial then. (I recall you wanted to do a more aggressive "fix mistakes of > the past" option at the time; I think this is mostly taking a second swing > along those lines.) It's not exactly fixing the mistakes of the past and more wanting to be able to refactor a switch expression to a switch statement back and forth without thinking too much if i recall correctly. > Note that we still have the alternative to do nothing: let the user be on their > own with totality for statement switches. That wasn't a terrible option in 12 > (which is why we pushed it off until now), and it's still not really terrible > now; it's just that the number of cases of nontrivial totality, and the > complexity of the remainder, has gone up and will go up more in the future > (enums yesterday; sealed types today; deconstruction patterns tomorrow), which > provides some additional pressure for a "total statement switch" option. Which > we could do now, or later, or never. yes, doing nothing is an option, but it means giving up on having an error reported by the compiler in a switch statement when you add a new subtypes to a sealed types, which is a kind of sad. R?mi > On 9/3/2020 3:30 PM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>> ?: "Remi Forax" [ mailto:forax at univ-mlv.fr | ] >>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>> ] >>> Envoy?: Jeudi 3 Septembre 2020 20:16:10 >>> Objet: Re: [pattern-switch] Opting into totality >>> That came up in the expression switch exploration. The thinking then, which I >>> think is still valid, that it is easier to understand the difference when >>> default-totality is attached to the expression versions, because expressions >>> _must_ be total and statements totally make sense to be partial. >> yes, that made sense at the time, the problem is that now we want seom statement >> switches to be total. >>> I think this is still coming from a place of retrospective snitch-envy; you want >>> to carve out a corner that has the "right" semantics, even if its relation to >>> the other corners is totally ad-hoc and random. >> It's not random, arrow switch avoid fallthrough in between cases but weirdly >> allow a to fallthrough the whole switch. >> So it's not about having the semantics right, it's about making the semantics >> consistent, here the story being able to fall to the next instruction is not >> consistent. >>> The upgrade to switch was driven by orthogonality; totality derives from whether >>> the context of the switch (statement vs expression) requires totality or >>> embraces partiality. >> Very true, but now you are saying that you want to introduce an opt-in to >> totality for the switch statement, so it's not orthogonal anymore. >>> And the kinds of labels are strictly about the treatment of what is on the RHS >>> -- either a single { expression/statement } vs complex control flow. Which is >>> orthogonal to expression/statement. >> The fact that you can implicitly skip the whole switch with arrows it's a >> complex control flow, , so that part is not fully orthogonal too. >>> So, I think we got it right then; we just have some holes to patch. >> The problem is that introducing a switch statement that requires totality goes >> full throttle against the idea that a statement switch implies partiallity. >> R?mi >>> On 9/3/2020 1:04 PM, Remi Forax wrote: >>>> I just want to say that the is yet another option, >>>> say that (statement and expression) arrow switches are always total. >>>> We have introduced the arrow notation to avoid fallthrough but we have forgotten >>>> one important case of fallthrough, in a statement switch when you skip the >>>> entire switch, you fallthrough the entire switch. >>>> So we keep supporting the traditional partial switch with no modification but >>>> requires if a user wants a partial arrow switch, to add a "default -> {}". >>>> This is an incompatible change with the codes written since Java 14 so it's a >>>> limited incompatible change. >>>> Perhaps the main blocker is admitting that we were wrong. >>>> R?mi >>>>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>>>> ?: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>>>> ] >>>>> Envoy?: Lundi 31 Ao?t 2020 15:25:13 >>>>> Objet: Re: [pattern-switch] Opting into totality >>>>> I think this is the main open question at this point. >>>>> We now have a deeper understanding of what this means, and the shape of the >>>>> remainder. Totality means not only ?spot check me that I?m right?, but also ?I >>>>> know there might be some remainder, please deal with it.? So totality is not >>>>> merely about type checking, but about affirmative handling of the remainder. >>>>> Expression switches automatically get this treatment, and opting _out_ of that >>>>> makes no sense for expression switches (expressions must be total), but >>>>> statement switches make sense both ways (just like unbalanced and balanced >>>>> if-else.) Unfortunately the default has to be partial, so the main question is, >>>>> how do we indicate the desire for totality in a way that is properly evocative >>>>> for the user? >>>>> We?ve talked about modifying switch (sealed switch), a hyphenated keyword >>>>> (total-switch), a trailing modifier (switch case), and synthetic cases >>>>> (?default: unreachable?). Of course at this point it?s ?just syntax?, but I >>>>> think our goal should be picking something that makes it obvious to users that >>>>> what?s going on is not merely an assertion of totality, but also a desire to >>>>> handle the remainder. >>>>>> - How does a switch opt into totality, other than by being an expression switch? -------------- next part -------------- An HTML attachment was scrubbed... URL: From guy.steele at oracle.com Fri Sep 4 02:24:24 2020 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 3 Sep 2020 22:24:24 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> Message-ID: There is currently a fundamental unpleasant asymmetry caused by the arbitrary decision to require that all switch expressions be total but not all switch statements be total. As we have added other options, the design space still has an unpleasant asymmetry (or lack of orthogonality) because of that original decision. We have two choices: (a) recant that original decision, or (b) live with the asymmetry. As I have already pointed out, (a) is certainly possible and completely consistent; all you have to do is return default values for the cases that are not covered. Then the choice of switch-versus-expression is completely independent of the choice of regular-switch versus total-switch. The likely result is that authors and IDEs will warn programmers that they should NEVER EVER use a non-total switch expression, so it seems silly to expand the design space to provide a combination of features that should never be used. (?What, never?? ?Well, hardly ever.?) The other choice, which is what R?mi and Brian have been discussing for the last week, is not whether to get rid of the asymmetry, but merely debating exactly what its shape should be. A possibility I don;?t think I have yet seen offered is that we should, as before, require switch expressions to be total, but also, in an incompatible move, require switch expressions to use whatever `total-switch` syntax is adopted. In such a scenario, perhaps we really are trying to move the world to `snitch` after all, and we?re just arguing about the least disruptive spelling for it. ?Guy From guy.steele at oracle.com Fri Sep 4 02:34:47 2020 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 3 Sep 2020 22:34:47 -0400 Subject: [pattern-switch] sealed switch In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> Message-ID: I just want to interject that I really dislike the terminology (and therefore the syntax) ?sealed switch? because it is too likely to confuse Joe Programmer (or me) into thinking that it necessarily makes use of, or is used in conjunction with, a sealed type. One might also misconstrue it to mean that the set of switch labels is fixed and cannot be added to later! I much prefer the story that it is a kind of switch that insists that _some_ switch label be chosen, rather than silently allowing no switch label to be matched. I happen to think that `switch case` is a better way to allude to that story, but there may be better ways. The story is more important than the syntax. In my mind, it is from this story that the requirement for totality is derived. From forax at univ-mlv.fr Fri Sep 4 06:47:21 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 4 Sep 2020 08:47:21 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: References: <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> Message-ID: <706050862.732021.1599202041741.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Guy Steele" > ?: "Remi Forax" , "Brian Goetz" > Cc: "amber-spec-experts" > Envoy?: Vendredi 4 Septembre 2020 04:24:24 > Objet: Re: [pattern-switch] Opting into totality > There is currently a fundamental unpleasant asymmetry caused by the arbitrary > decision to require that all switch expressions be total but not all switch > statements be total. > > As we have added other options, the design space still has an unpleasant > asymmetry (or lack of orthogonality) because of that original decision. > > We have two choices: (a) recant that original decision, or (b) live with the > asymmetry. > > As I have already pointed out, (a) is certainly possible and completely > consistent; all you have to do is return default values for the cases that are > not covered. Then the choice of switch-versus-expression is completely > independent of the choice of regular-switch versus total-switch. The likely > result is that authors and IDEs will warn programmers that they should NEVER > EVER use a non-total switch expression, so it seems silly to expand the design > space to provide a combination of features that should never be used. (?What, > never?? ?Well, hardly ever.?) > > The other choice, which is what R?mi and Brian have been discussing for the last > week, is not whether to get rid of the asymmetry, but merely debating exactly > what its shape should be. yes > > A possibility I don;?t think I have yet seen offered is that we should, as > before, require switch expressions to be total, but also, in an incompatible > move, require switch expressions to use whatever `total-switch` syntax is > adopted. > > In such a scenario, perhaps we really are trying to move the world to `snitch` > after all, and we?re just arguing about the least disruptive spelling for it. yep, it's a snitch move, but instead of calling it "when" or whatever, we use a hyphenated keyword like "total-switch", "sealed-switch" or double words keyword like "sealed switch". I think i'm not at ease with this proposed change because it goes against the design idea that leads to the current semantics of the switch expression, the fact that a statement switch is partial while an expression switch is total. > > ?Guy R?mi From brian.goetz at oracle.com Fri Sep 4 11:08:37 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Sep 2020 07:08:37 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> Message-ID: <86AD4794-79DF-4DE2-AAB2-897746C1D5DC@oracle.com> I agree with the ?unpleasant asymmetry? part, but I?m not sure its fair to call it arbitrary. The original design of switch statements deliberately embraced partiality, for non-arbitrary reasons; the design of expression switches deliberately embraced totality, again for non-arbitrary reasons (and with awareness of the ensuing asymmetry.) I think it is more accurate to attribute the asymmetry not to arbitrary choices, but deliberate ones, specifically: the decision to reform switch, rather than create a new-and-almost-identical-but-not-identical construct (?snitch?), and have the two (or four or eight, as we discovered new mistakes) live side-by-side forever. And, while there were many ?quick-lets-fix-the-mistakes-of-the-past" suggestions for the ideal snitch semantics, I suspect that had we designed a snitch in 12, we still would have faced uncomfortable evolutionary challenges now. (For the record: I am still convinced that reform, rather than replacement, is the right move.) We knew the cost would be some asymmetries and some uncomfortable special cases (such as the current quarantining of preemptive null-hostility to the three legacy reference target types, enums/strings/boxes.) But these costs are still far lower (IMO) than having two almost-the-same-but-not constructs living side-by-side forever, a burning hulk by the side of the road that is never extinguished. At some level, the current discussion is an exploration of how much of our cake we can have and eat as well. When we did expressions switches, we knew the totality asymmetry was not a solved problem. For the limited sorts of non-strongly-total switches we had then (just enum switches), doing nothing at the time, and keeping the option to do something later, was the obvious and right move. As switches get more powerful, there are more kinds of non-strongly-total switches (switches over sealed types, switches with deconstruction patterns lifted over total pattern groups, etc), and the desire for some extra type checking (and automated remainder handling) becomes greater. For the current round (type patterns in switch), I think its probably practical to still do nothing now; I think the real need comes around when we get to deconstruction and nested patterns. But none of the options discussed are perishable; the things that are compatible now will be compatible later, and the things that are incompatible now will still be incompatible later. > I just want to interject that I really dislike the terminology (and therefore the syntax) ?sealed switch? because it is too likely to confuse Joe Programmer (or me) into thinking that it necessarily makes use of, or is used in conjunction with, a sealed type. One might also misconstrue it to mean that the set of switch labels is fixed and cannot be added to later! I agree that the reuse of ?sealed? here is in the clever-possibly-too-clever category, and am happy to keep searching for something better. I?ll just reiterate that there is a nice accidental connection to sealed-ness ? that such switches are not ?leaky?. Sealing a switch means that there will be no values that are passed through and not acted upon. I bring this up again not to defend the syntax, but to underscore a more important point: that we thought at first that ?sealing? switches was about asserting totality and enlisting the compiler?s aid in verifying same, but as we worked through the cases, and discovered there were more cases of remainder than we initially thought, the main value of sealing instead became restoring the switch to having no unhandled remainder. Whatever syntax we choose should try to evoke that at least as much as evoking totality of the case labels. > On Sep 3, 2020, at 10:24 PM, Guy Steele wrote: > > There is currently a fundamental unpleasant asymmetry caused by the arbitrary decision to require that all switch expressions be total but not all switch statements be total. > > As we have added other options, the design space still has an unpleasant asymmetry (or lack of orthogonality) because of that original decision. > > We have two choices: (a) recant that original decision, or (b) live with the asymmetry. > > As I have already pointed out, (a) is certainly possible and completely consistent; all you have to do is return default values for the cases that are not covered. Then the choice of switch-versus-expression is completely independent of the choice of regular-switch versus total-switch. The likely result is that authors and IDEs will warn programmers that they should NEVER EVER use a non-total switch expression, so it seems silly to expand the design space to provide a combination of features that should never be used. (?What, never?? ?Well, hardly ever.?) > > The other choice, which is what R?mi and Brian have been discussing for the last week, is not whether to get rid of the asymmetry, but merely debating exactly what its shape should be. > > A possibility I don;?t think I have yet seen offered is that we should, as before, require switch expressions to be total, but also, in an incompatible move, require switch expressions to use whatever `total-switch` syntax is adopted. > > In such a scenario, perhaps we really are trying to move the world to `snitch` after all, and we?re just arguing about the least disruptive spelling for it. > > ?Guy > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Sep 4 18:06:50 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Sep 2020 14:06:50 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: <86AD4794-79DF-4DE2-AAB2-897746C1D5DC@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> <86AD4794-79DF-4DE2-AAB2-897746C1D5DC@oracle.com> Message-ID: > I bring this up again not to defend the syntax, but to underscore a > more important point: that we thought at first that ?sealing? switches > was about asserting totality and enlisting the compiler?s aid in > verifying same, but as we worked through the cases, and discovered > there were more cases of remainder than we initially thought, the main > value of sealing instead became restoring the switch to having no > unhandled remainder. ?Whatever syntax we choose should try to evoke > that at least as much as evoking totality of the case labels. Taking this a little further: it is not that interesting to declare a switch `sealed` that has a default clause or other total pattern; such switches are "obviously" total and non-leaky.? It's nice, but we wouldn't add a language feature for it. The interesting part of sealing a switch are cases like this. Assume Shape = Circ | Rect. ??? switch (shape) { ??????? case Circ c: ??????? case Rect r: ??? } Here, sealing gives us two things: asserting we've covered all the cases (which is not going to be obvious except in the most trivial of sealed types) and handling of remainder (here, only null.) Similarly: ??? switch (boxOfShape) { ??????? case Box(Circ c): ??????? case Box(Rect r): ??? } Same basic story -- not obvious that we've covered all boxes, the remainder is just slightly more complicated. The compile-time checking for optimistic totality, and runtime checking for remainder, are two sides of the same coin; we are stating expectations that all possible inputs are covered, either by an explicit case or by an implicit remainder case.? We statically check the ones we can, and dynamically reject the "acceptable non-coverage."? (Characterizing the acceptable non-coverage is the main bit of positive progress we've made recently.) I think we may be skipping over a step by jumping to "throwing syntax at the problem."? The real question is, what do we want the users thinking about when they are coding, and what should we not be bothering them with?? We've already acknowledged that we _don't_ want them worrying about explicitly managing the remainder. We've been approaching this as "I assert that this switch is sufficiently total", which included both strongly total and optimistically total switches (because that's what we already do for expression switches.)? But given that having a default case makes it obviously total (and obviates the need for remainder handling), perhaps we want to focus on the user asserting "I know I didn't write a default, but I am still covering all the cases."? Does that offer a different enough viewpoint that other options come into focus? From brian.goetz at oracle.com Fri Sep 4 21:29:43 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Sep 2020 17:29:43 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> <86AD4794-79DF-4DE2-AAB2-897746C1D5DC@oracle.com> Message-ID: <5a4fb083-12c3-2d5f-4cc0-a518c04c9195@oracle.com> Another alternative that I'll put in for the record, but which is brittle, is: ?- If the set of patterns of a statement switch is total with nonempty remainder R, insert a throwing default for R. This means that in ??? switch (box) { ??????? case Box(var x): ... ??? } which is total on Box with remainder { null }, we'd get an NPE on a null box.? Essentially, we infer totality for statement switches, and remainder-rejection comes with the totality. The brittleness comes from the fact that, if you miss a case of a sealed type, not only do you not find out your switch isn't total any more, but your remainder rejection goes away.? I don't like it, but this reframing might point to a slightly different way to say "I'm assuming totality, tell me if I'm wrong." On 9/4/2020 2:06 PM, Brian Goetz wrote: > >> I bring this up again not to defend the syntax, but to underscore a >> more important point: that we thought at first that ?sealing? >> switches was about asserting totality and enlisting the compiler?s >> aid in verifying same, but as we worked through the cases, and >> discovered there were more cases of remainder than we initially >> thought, the main value of sealing instead became restoring the >> switch to having no unhandled remainder. ?Whatever syntax we choose >> should try to evoke that at least as much as evoking totality of the >> case labels. > > Taking this a little further: it is not that interesting to declare a > switch `sealed` that has a default clause or other total pattern; such > switches are "obviously" total and non-leaky.? It's nice, but we > wouldn't add a language feature for it. > > The interesting part of sealing a switch are cases like this. Assume > Shape = Circ | Rect. > > ??? switch (shape) { > ??????? case Circ c: > ??????? case Rect r: > ??? } > > Here, sealing gives us two things: asserting we've covered all the > cases (which is not going to be obvious except in the most trivial of > sealed types) and handling of remainder (here, only null.) Similarly: > > ??? switch (boxOfShape) { > ??????? case Box(Circ c): > ??????? case Box(Rect r): > ??? } > > Same basic story -- not obvious that we've covered all boxes, the > remainder is just slightly more complicated. > > The compile-time checking for optimistic totality, and runtime > checking for remainder, are two sides of the same coin; we are stating > expectations that all possible inputs are covered, either by an > explicit case or by an implicit remainder case.? We statically check > the ones we can, and dynamically reject the "acceptable > non-coverage."? (Characterizing the acceptable non-coverage is the > main bit of positive progress we've made recently.) > > I think we may be skipping over a step by jumping to "throwing syntax > at the problem."? The real question is, what do we want the users > thinking about when they are coding, and what should we not be > bothering them with?? We've already acknowledged that we _don't_ want > them worrying about explicitly managing the remainder. > > We've been approaching this as "I assert that this switch is > sufficiently total", which included both strongly total and > optimistically total switches (because that's what we already do for > expression switches.)? But given that having a default case makes it > obviously total (and obviates the need for remainder handling), > perhaps we want to focus on the user asserting "I know I didn't write > a default, but I am still covering all the cases." Does that offer a > different enough viewpoint that other options come into focus? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Sep 4 21:51:54 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Sep 2020 17:51:54 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> Message-ID: <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> Let me add another option to the menu.? I'm not sure I like it, but it's less bad than many of the alternatives suggested, and not incompatible (but has a more complex compatibility boundary), so worth putting on the table. Remi suggested: > say that (statement and expression) arrow switches are always total. Coupling this to arrow switches only was definitely the wrong axis on which to cut this, but there might be another that isn't so bad: say that switches over types other than { primitives, boxes, strings, enums } are always total, and we ask users to totalize otherwise-partial switches with `default: `.? Alternately, we could couple this not to the type, but to switches with _any non-constant cases_.? (This seems better than keying off of the type.) Then we can optionally combine it with the (not so good) idea in the previous mail -- implicit remainder handling -- which becomes a better idea in this context, since it only comes into play when an optimistically total but not strongly total set of cases is present. So we have { expression, statement } x { arrow, colon } switches, and the totality rules are: a switch is total if it is an expression switch, or it has any non-constant patterns.? Total switches then get an implicit throwing default if they have no strongly total pattern.? That's a kind of irregular shape, but possibly justifiable. I'm not sure I like it, because I am not yet convinced that partial pattern statement switches? won't be common, but I'll have to think about it.? It is definitely a bigger mental shift for users about what switch means. On 9/3/2020 2:16 PM, Brian Goetz wrote: > That came up in the expression switch exploration.? The thinking then, > which I think is still valid, that it is easier to understand the > difference when default-totality is attached to the expression > versions, because expressions _must_ be total and statements totally > make sense to be partial. > > I think this is still coming from a place of retrospective > snitch-envy; you want to carve out a corner that has the "right" > semantics, even if its relation to the other corners is totally ad-hoc > and random.? The upgrade to switch was driven by orthogonality; > totality derives from whether the context of the switch (statement vs > expression) requires totality or embraces partiality.? And the kinds > of labels are strictly about the treatment of what is on the RHS -- > either a single { expression/statement } vs complex control flow. > Which is orthogonal to expression/statement. > > So, I think we got it right then; we just have some holes to patch. > > On 9/3/2020 1:04 PM, Remi Forax wrote: >> I just want to say that the is yet another option, >> say that (statement and expression) arrow switches are always total. >> >> We have introduced the arrow notation to avoid fallthrough but we >> have forgotten one important case of fallthrough, in a statement >> switch when you skip the entire switch, you fallthrough the entire >> switch. >> >> So we keep supporting the traditional partial switch with no >> modification but requires if a user wants a partial arrow switch, to >> add a "default -> {}". >> >> This is an incompatible change with the codes written since Java 14 >> so it's a limited incompatible change. >> Perhaps the main blocker is admitting that we were wrong. >> >> R?mi >> >> ------------------------------------------------------------------------ >> >> *De: *"Brian Goetz" >> *?: *"amber-spec-experts" >> *Envoy?: *Lundi 31 Ao?t 2020 15:25:13 >> *Objet: *Re: [pattern-switch] Opting into totality >> >> I think this is the main open question at this point. >> >> We now have a deeper understanding of what this means, and the >> shape of the remainder. ?Totality means not only ?spot check me >> that I?m right?, but also ?I know there might be some remainder, >> please deal with it.? ? So totality is not merely about type >> checking, but about affirmative handling of the remainder. >> >> Expression switches automatically get ?this treatment, and opting >> _out_ of that makes no sense for expression switches (expressions >> must be total), but statement switches make sense both ways (just >> like unbalanced and balanced if-else.) ?Unfortunately the default >> has to be partial, ?so the main question is, how ?do we indicate >> the desire for totality in a way that is properly evocative for >> the user? >> >> We?ve talked about modifying switch (sealed switch), a hyphenated >> keyword (total-switch), a trailing modifier (switch case), and >> synthetic cases (?default: unreachable?). ?Of course at this >> point it?s ?just syntax?, but I think our goal should be picking >> something that ?makes it obvious to users that what?s going on is >> not merely an assertion of totality, but also a desire to handle >> the remainder. >> >> ? ?- How does a switch opt into totality, other than by being >> an expression switch? >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Fri Sep 4 22:29:05 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 4 Sep 2020 16:29:05 -0600 Subject: [pattern-switch] Opting into totality In-Reply-To: <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> Message-ID: > On Sep 4, 2020, at 3:29 PM, Brian Goetz wrote: > > The brittleness comes from the fact that, if you miss a case of a sealed type, not only do you not find out your switch isn't total any more, but your remainder rejection goes away. And this isn't just a problem when you write the switch. Adding cases to an enum is a common thing to do; I assume adding cases to a sealed class will be, too. When that happens, a previously-total switch will silently start to fall out. > On Sep 4, 2020, at 3:51 PM, Brian Goetz wrote: > > So we have { expression, statement } x { arrow, colon } switches, and the totality rules are: a switch is total if it is an expression switch, or it has any non-constant patterns. Total switches then get an implicit throwing default if they have no strongly total pattern. That's a kind of irregular shape, but possibly justifiable. Things that I dislike here: - Switch statements don't support optimistically-total switching on enums. Meanwhile, switch expressions *do* support optimistically-total switching on enums. It's a great feature. - To do an "if without else" switch with constants, you leave off the default. To do an "if without else" switch with patterns, you have to include the default (with an empty body). Seems like noise to me, but more importantly, it's hard to remember which one to do. As you suggest, it just seems really hard to keep track of these ad hoc rules. Meanwhile, providing some syntax to assert that a switch should get total treatment, and claiming that switch expressions have this property implicitly, seems totally fine to me. It's consistent (oops, I used that word) with, say, how things declared in interfaces have certain implicit properties. So I appreciate the brainstorming, but personally don't really see a problem that needs solving here. (Other than the syntax bikeshed.) From forax at univ-mlv.fr Sun Sep 6 10:11:41 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 6 Sep 2020 12:11:41 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> Message-ID: <1884853861.1610931.1599387101348.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Vendredi 4 Septembre 2020 23:51:54 > Objet: Re: [pattern-switch] Opting into totality > Let me add another option to the menu. I'm not sure I like it, but it's less bad > than many of the alternatives suggested, and not incompatible (but has a more > complex compatibility boundary), so worth putting on the table. > Remi suggested: > > say that (statement and expression) arrow switches are always total. > Coupling this to arrow switches only was definitely the wrong axis on which to > cut this, but there might be another that isn't so bad: say that switches over > types other than { primitives, boxes, strings, enums } are always total, and we > ask users to totalize otherwise-partial switches with `default: nothing>`. Alternately, we could couple this not to the type, but to switches > with _any non-constant cases_. (This seems better than keying off of the type.) yes, any non-constant cases is better partitioning than switch on types. I believe the same partitioning/divider can be use for the nullable/non-nullable switch, i.e. a switch with only constant cases (and an optional default) reject null, all the others are nullable. > Then we can optionally combine it with the (not so good) idea in the previous > mail -- implicit remainder handling -- which becomes a better idea in this > context, since it only comes into play when an optimistically total but not > strongly total set of cases is present. > So we have { expression, statement } x { arrow, colon } switches, and the > totality rules are: a switch is total if it is an expression switch, or it has > any non-constant patterns. Total switches then get an implicit throwing default > if they have no strongly total pattern. That's a kind of irregular shape, but > possibly justifiable. > I'm not sure I like it, because I am not yet convinced that partial pattern > statement switches won't be common, but I'll have to think about it. It is > definitely a bigger mental shift for users about what switch means. I think i still prefer an explicit keyword at the end of the statement switch, let's call it "no-default", semantically equivalent to case var x: if (x == null) throw null; // if x is nullable, so not for primitive and inline in the future throw new ICCE(); but the compiler checks that the switch without "no-default" is optimistically total. so switch(shape) { case Circle: ... case Rect: ... no-default; } is total. and switch(object) { case Foo: default: ... no-default; } is not valid because no-default is not reachable. And if we keep the idea that a switch with a least a non-constant case has to be total, no-default is invalid in a switch expression and in a switch with a non-constant case. R?mi > On 9/3/2020 2:16 PM, Brian Goetz wrote: >> That came up in the expression switch exploration. The thinking then, which I >> think is still valid, that it is easier to understand the difference when >> default-totality is attached to the expression versions, because expressions >> _must_ be total and statements totally make sense to be partial. >> I think this is still coming from a place of retrospective snitch-envy; you want >> to carve out a corner that has the "right" semantics, even if its relation to >> the other corners is totally ad-hoc and random. The upgrade to switch was >> driven by orthogonality; totality derives from whether the context of the >> switch (statement vs expression) requires totality or embraces partiality. And >> the kinds of labels are strictly about the treatment of what is on the RHS -- >> either a single { expression/statement } vs complex control flow. Which is >> orthogonal to expression/statement. >> So, I think we got it right then; we just have some holes to patch. >> On 9/3/2020 1:04 PM, Remi Forax wrote: >>> I just want to say that the is yet another option, >>> say that (statement and expression) arrow switches are always total. >>> We have introduced the arrow notation to avoid fallthrough but we have forgotten >>> one important case of fallthrough, in a statement switch when you skip the >>> entire switch, you fallthrough the entire switch. >>> So we keep supporting the traditional partial switch with no modification but >>> requires if a user wants a partial arrow switch, to add a "default -> {}". >>> This is an incompatible change with the codes written since Java 14 so it's a >>> limited incompatible change. >>> Perhaps the main blocker is admitting that we were wrong. >>> R?mi >>>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>>> ?: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>>> ] >>>> Envoy?: Lundi 31 Ao?t 2020 15:25:13 >>>> Objet: Re: [pattern-switch] Opting into totality >>>> I think this is the main open question at this point. >>>> We now have a deeper understanding of what this means, and the shape of the >>>> remainder. Totality means not only ?spot check me that I?m right?, but also ?I >>>> know there might be some remainder, please deal with it.? So totality is not >>>> merely about type checking, but about affirmative handling of the remainder. >>>> Expression switches automatically get this treatment, and opting _out_ of that >>>> makes no sense for expression switches (expressions must be total), but >>>> statement switches make sense both ways (just like unbalanced and balanced >>>> if-else.) Unfortunately the default has to be partial, so the main question is, >>>> how do we indicate the desire for totality in a way that is properly evocative >>>> for the user? >>>> We?ve talked about modifying switch (sealed switch), a hyphenated keyword >>>> (total-switch), a trailing modifier (switch case), and synthetic cases >>>> (?default: unreachable?). Of course at this point it?s ?just syntax?, but I >>>> think our goal should be picking something that makes it obvious to users that >>>> what?s going on is not merely an assertion of totality, but also a desire to >>>> handle the remainder. >>>>> - How does a switch opt into totality, other than by being an expression switch? -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 6 12:01:49 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 6 Sep 2020 14:01:49 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <5a4fb083-12c3-2d5f-4cc0-a518c04c9195@oracle.com> References: <1000677859.669764.1599161408966.JavaMail.zimbra@u-pem.fr> <927163448.673359.1599162885029.JavaMail.zimbra@u-pem.fr> <86AD4794-79DF-4DE2-AAB2-897746C1D5DC@oracle.com> <5a4fb083-12c3-2d5f-4cc0-a518c04c9195@oracle.com> Message-ID: <646144924.1621604.1599393709913.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Guy Steele" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Vendredi 4 Septembre 2020 23:29:43 > Objet: Re: [pattern-switch] Opting into totality > Another alternative that I'll put in for the record, but which is brittle, is: > - If the set of patterns of a statement switch is total with nonempty remainder > R, insert a throwing default for R. > This means that in > switch (box) { > case Box(var x): ... > } > which is total on Box with remainder { null }, we'd get an NPE on a null box. > Essentially, we infer totality for statement switches, and remainder-rejection > comes with the totality. > The brittleness comes from the fact that, if you miss a case of a sealed type, > not only do you not find out your switch isn't total any more, but your > remainder rejection goes away. I don't like it, but this reframing might point > to a slightly different way to say "I'm assuming totality, tell me if I'm > wrong." I believe it's not a backward compatible change, enum Foo {A, B} switch(foo) { case A: ... case B: ... } throw new AssertionError(); This code is currently valid by with your proposal, the throw new AssertionError() at the end becomes unreachable. R?mi > On 9/4/2020 2:06 PM, Brian Goetz wrote: >>> I bring this up again not to defend the syntax, but to underscore a more >>> important point: that we thought at first that ?sealing? switches was about >>> asserting totality and enlisting the compiler?s aid in verifying same, but as >>> we worked through the cases, and discovered there were more cases of remainder >>> than we initially thought, the main value of sealing instead became restoring >>> the switch to having no unhandled remainder. Whatever syntax we choose should >>> try to evoke that at least as much as evoking totality of the case labels. >> Taking this a little further: it is not that interesting to declare a switch >> `sealed` that has a default clause or other total pattern; such switches are >> "obviously" total and non-leaky. It's nice, but we wouldn't add a language >> feature for it. >> The interesting part of sealing a switch are cases like this. Assume Shape = >> Circ | Rect. >> switch (shape) { >> case Circ c: >> case Rect r: >> } >> Here, sealing gives us two things: asserting we've covered all the cases (which >> is not going to be obvious except in the most trivial of sealed types) and >> handling of remainder (here, only null.) Similarly: >> switch (boxOfShape) { >> case Box(Circ c): >> case Box(Rect r): >> } >> Same basic story -- not obvious that we've covered all boxes, the remainder is >> just slightly more complicated. >> The compile-time checking for optimistic totality, and runtime checking for >> remainder, are two sides of the same coin; we are stating expectations that all >> possible inputs are covered, either by an explicit case or by an implicit >> remainder case. We statically check the ones we can, and dynamically reject the >> "acceptable non-coverage." (Characterizing the acceptable non-coverage is the >> main bit of positive progress we've made recently.) >> I think we may be skipping over a step by jumping to "throwing syntax at the >> problem." The real question is, what do we want the users thinking about when >> they are coding, and what should we not be bothering them with? We've already >> acknowledged that we _don't_ want them worrying about explicitly managing the >> remainder. >> We've been approaching this as "I assert that this switch is sufficiently >> total", which included both strongly total and optimistically total switches >> (because that's what we already do for expression switches.) But given that >> having a default case makes it obviously total (and obviates the need for >> remainder handling), perhaps we want to focus on the user asserting "I know I >> didn't write a default, but I am still covering all the cases." Does that offer >> a different enough viewpoint that other options come into focus? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 6 16:53:36 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 6 Sep 2020 12:53:36 -0400 Subject: [pattern-switch] Opting into totality In-Reply-To: References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> Message-ID: <8ae92721-bd6e-913a-2745-9bd674d6fc25@oracle.com> > So I appreciate the brainstorming, but personally don't really see a problem that needs solving here. (Other than the syntax bikeshed.) Agreed, it was worth poking around the corners with a flashlight, but the exploration brought me back to this point too.? (If we even need it at all.) Some of the motivation for this exercise has been that some people feel uncomfortable at inferred totality.? But, there's a good chance that this discomfort is temporary -- this happens every time any sort of implicitness is added to the language.?? (People freaked out about var at first too.) From forax at univ-mlv.fr Mon Sep 7 08:20:48 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 7 Sep 2020 10:20:48 +0200 (CEST) Subject: [pattern-switch] Opting into totality In-Reply-To: <8ae92721-bd6e-913a-2745-9bd674d6fc25@oracle.com> References: <1e3058dd-b98e-cffe-371a-b395fb768838@oracle.com> <2B1A968E-C518-496D-984E-76A05B2B545B@oracle.com> <630127997.639840.1599152672042.JavaMail.zimbra@u-pem.fr> <1398e80f-c352-68fb-3351-0b658c6c9b49@oracle.com> <8ae92721-bd6e-913a-2745-9bd674d6fc25@oracle.com> Message-ID: <175127839.159969.1599466848832.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "daniel smith" > Cc: "Remi Forax" , "amber-spec-experts" > Envoy?: Dimanche 6 Septembre 2020 18:53:36 > Objet: Re: [pattern-switch] Opting into totality >> So I appreciate the brainstorming, but personally don't really see a problem >> that needs solving here. (Other than the syntax bikeshed.) > > Agreed, it was worth poking around the corners with a flashlight, but > the exploration brought me back to this point too.? (If we even need it > at all.) > > Some of the motivation for this exercise has been that some people feel > uncomfortable at inferred totality.? But, there's a good chance that > this discomfort is temporary -- this happens every time any sort of > implicitness is added to the language.?? (People freaked out about var > at first too.) at least for me, frame it like that doesn't help, the issue is accidental totality (or accidental non-totality) in a switch statement no problem with neither inferred totality nor with the switch expression. and to refine the question of this thread, i don't think we need to opt in to full totality, what is needed it an opt in to optimistic totality, adding a default or a case var x is enough otherwise. R?mi From forax at univ-mlv.fr Mon Sep 7 08:23:43 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 7 Sep 2020 10:23:43 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? Message-ID: <218302326.163193.1599467023001.JavaMail.zimbra@u-pem.fr> Just a question, do we agree that the syntax below is valid ? Point point = ... switch(point) { case var(var x, var y): ... } i.e. that var can be written everywhere there is a type in a Pattern. R?mi From amalloy at google.com Mon Sep 7 09:36:09 2020 From: amalloy at google.com (Alan Malloy) Date: Mon, 7 Sep 2020 02:36:09 -0700 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <218302326.163193.1599467023001.JavaMail.zimbra@u-pem.fr> References: <218302326.163193.1599467023001.JavaMail.zimbra@u-pem.fr> Message-ID: I would be very surprised if that were valid. The inner vars are fine, of course. However, your outer one has not replaced a type, but a deconstructor reference, or whatever we're calling the opposite of a constructor. "Any object which can be deconstructed into two constituent objects" will surely not be a useful query very often, and I wouldn't expect the language to support it. On Mon, Sep 7, 2020, 1:24 AM Remi Forax wrote: > Just a question, > do we agree that the syntax below is valid ? > > Point point = ... > switch(point) { > case var(var x, var y): ... > } > > i.e. that var can be written everywhere there is a type in a Pattern. > > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Sep 7 13:23:38 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 7 Sep 2020 09:23:38 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <218302326.163193.1599467023001.JavaMail.zimbra@u-pem.fr> References: <218302326.163193.1599467023001.JavaMail.zimbra@u-pem.fr> Message-ID: <0BBEC7D4-B886-4F7A-818F-C40290FAC7E2@oracle.com> No. Definitely not. I don?t even know what you would propose that to mean! I have a guess, but I?m order for that guess to even make sense, it would assume a model for how patterns are declared that is very different from what we have now. There are about a zillion places where you can use types and can?t use var: array elements, import statement, type parameters, etc. this is just one of those. Sent from my iPad > On Sep 7, 2020, at 4:28 AM, Remi Forax wrote: > > ?Just a question, > do we agree that the syntax below is valid ? > > Point point = ... > switch(point) { > case var(var x, var y): ... > } > > i.e. that var can be written everywhere there is a type in a Pattern. > > R?mi From guy.steele at oracle.com Mon Sep 7 15:51:06 2020 From: guy.steele at oracle.com (Guy Steele) Date: Mon, 7 Sep 2020 11:51:06 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: Message-ID: I agree with Alan. While I believe that R?mi is correct insofar as you can write ?var? in place of a type in any type pattern ?T x?, in a deconstruction pattern ?P(...) [d]? the occurrence of P is not a type; rather, it names a deconstructor. It does so happen that right now all deconstructors (like all constructors) share the name of an associated type, but it is important not to confuse them. You cannot replace a deconstructor name with ?var? any more than you can write ?new var()? or ?new MyInterface()?. ?Guy > On Sep 7, 2020, at 5:36 AM, Alan Malloy wrote: > > ? > I would be very surprised if that were valid. The inner vars are fine, of course. However, your outer one has not replaced a type, but a deconstructor reference, or whatever we're calling the opposite of a constructor. "Any object which can be deconstructed into two constituent objects" will surely not be a useful query very often, and I wouldn't expect the language to support it. > >> On Mon, Sep 7, 2020, 1:24 AM Remi Forax wrote: >> Just a question, >> do we agree that the syntax below is valid ? >> >> Point point = ... >> switch(point) { >> case var(var x, var y): ... >> } >> >> i.e. that var can be written everywhere there is a type in a Pattern. >> >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Sep 7 16:55:41 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 7 Sep 2020 12:55:41 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: Message-ID: <0630c492-38e7-bba9-3733-b521cd88ed69@oracle.com> > I agree with Alan. While I believe that R?mi is correct insofar as you > can write ?var? in place of a type in any type pattern ?T x?, in a > deconstruction pattern ?P(...) [d]? the occurrence of P is not a type; > rather, it names a deconstructor. Exactly right. > It does so happen that right now all deconstructors (like all > constructors) share the name of an associated type, but it is > important not to confuse them. You cannot replace a deconstructor name > with ?var? any more than you can write ?new var()? or ?new > MyInterface()?. or `new var[3]` or `import var` or `Foo = e`. From forax at univ-mlv.fr Mon Sep 7 17:03:27 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 7 Sep 2020 19:03:27 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: Message-ID: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> > De: "Guy Steele" > ?: "Alan Malloy" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Lundi 7 Septembre 2020 17:51:06 > Objet: Re: Is case var(var x, var y) a valid syntax ? > I agree with Alan. While I believe that R?mi is correct insofar as you can write > ?var? in place of a type in any type pattern ?T x?, in a deconstruction pattern > ?P(...) [d]? the occurrence of P is not a type; rather, it names a > deconstructor. It does so happen that right now all deconstructors (like all > constructors) share the name of an associated type, but it is important not to > confuse them. You cannot replace a deconstructor name with ?var? any more than > you can write ?new var()? or ?new MyInterface()?. hum, technically you can write new MyInterface() { ... } I disagree with that rational because a deconstructor is an instance method, so you need to do an instanceof first, said differently P(...) is a deconstruction pattern which is equivalent to instanceof P p && var values = p.__name_of_the_deconstructor() > From Brian, > There are about a zillion places where you can use types and can?t use var: array elements, import statement, type parameters, etc. this is just one of those. There is a good reason to not use var for all of them, i believe. > ?Guy R?mi >> On Sep 7, 2020, at 5:36 AM, Alan Malloy wrote: >> I would be very surprised if that were valid. The inner vars are fine, of >> course. However, your outer one has not replaced a type, but a deconstructor >> reference, or whatever we're calling the opposite of a constructor. "Any object >> which can be deconstructed into two constituent objects" will surely not be a >> useful query very often, and I wouldn't expect the language to support it. >> On Mon, Sep 7, 2020, 1:24 AM Remi Forax < [ mailto:forax at univ-mlv.fr | >> forax at univ-mlv.fr ] > wrote: >>> Just a question, >>> do we agree that the syntax below is valid ? >>> Point point = ... >>> switch(point) { >>> case var(var x, var y): ... >>> } >>> i.e. that var can be written everywhere there is a type in a Pattern. >>> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Sep 7 17:46:51 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 7 Sep 2020 13:46:51 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> Message-ID: > I disagree with that rational because a deconstructor is an instance method, so you need to do an instanceof first, > said differently P(...) is a deconstruction pattern which is equivalent to instanceof P p && var values = p.__name_of_the_deconstructor() At root, I think a lot of your frustration is that you are imagining a different model for what pattern matching means in the language. (I?m sure there are many different possible ways we could add pattern matching to Java.) In fact, I think you are not disagreeing with the rationale, you are disagreeing fundamentally with the model of how deconstruction patterns work in the current model. Deconstruction patterns are declared members, like constructors. The language arranges for them to be invoked as part of core linguistic operations (object instantiation for constructors, pattern matching for deconstructors.) I get that you have another model in mind, but if you want to convince people yours is better, you have to put the model on the table. Believe me, writing this up clearly, consistently, and completely is not a small task, but if you want to move the design, that?s the first step. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Sep 7 21:56:05 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 7 Sep 2020 23:56:05 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> Message-ID: <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "Guy Steele" , "Alan Malloy" , > "amber-spec-experts" > Envoy?: Lundi 7 Septembre 2020 19:46:51 > Objet: Re: Is case var(var x, var y) a valid syntax ? >> I disagree with that rational because a deconstructor is an instance method, so >> you need to do an instanceof first, >> said differently P(...) is a deconstruction pattern which is equivalent to >> instanceof P p && var values = p.__name_of_the_deconstructor() > At root, I think a lot of your frustration is that you are imagining a different > model for what pattern matching means in the language. (I?m sure there are many > different possible ways we could add pattern matching to Java.) > In fact, I think you are not disagreeing with the rationale, you are disagreeing > fundamentally with the model of how deconstruction patterns work in the current > model. Deconstruction patterns are declared members, like constructors. The > language arranges for them to be invoked as part of core linguistic operations > (object instantiation for constructors, pattern matching for deconstructors.) I > get that you have another model in mind, but if you want to convince people > yours is better, you have to put the model on the table. Believe me, writing > this up clearly, consistently, and completely is not a small task, but if you > want to move the design, that?s the first step. The deconstruction pattern is a special case of type pattern, both starts by doing a typecheck (an instanceof), what is different is how they bind variables after the typecheck. So having the type inference on the type used by the typecheck working for the type pattern but not for the destruction pattern strike me as weird. At the same time, i understand why people can find the syntax awkward. I just want to be sure that it's not awkward just because it's a new syntax. By example, with this switch, that has two cases that are semantically identical switch(point) { case Point p where p.x() == 0 -> ... case Point(var x, var _) where x == 0 -> ... } one may want to use inference switch(point) { case var p where p.x() == 0 -> ... // this one is Ok ! case var(var x, var _) where x == 0 -> ... // this one is Not ? } How the deconstructors are represented in the surface language, how they are called or how the data values flow to the bindings are another stories for another time. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Sep 7 22:52:11 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 7 Sep 2020 18:52:11 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> Message-ID: > > The deconstruction pattern is a special case of type pattern No, it isn't.? I think you want it to be, but it isn't. > , both starts by doing a typecheck (an instanceof), what is different > is how they bind variables after the typecheck. > So having the type inference on the type used by the typecheck working > for the type pattern but not for the destruction pattern strike me as > weird. I think you may just not understand the model here. In generality, a pattern: ?- Is either total or partial.? Deconstruction patterns are total; other patterns we'll be able to express later, such as `Optional.of(var x)`, are partial. ?- Has a target type.? In order for the pattern to match, the dynamic type of the target must be cast-convertible to this target type. ?- Has a set of output bindings. So, if the user says: ??? case Foo(int x): This means: ?- (static) perform overload selection on the deconstructors of Foo, and look for one that is compatible with the binding list `(int x)`.? Compile error if there isn't one (or are too many.) ?- (dynamic) test the target to see if it is cast-convertible to Foo.? If it is, because the pattern is total (no additional match conditions), the deconstruction pattern is going to match.? Invoke the deconstructor to get the bindings. > At the same time, i understand why people can find the syntax awkward. > I just want to be sure that it's not awkward just because it's a new > syntax. It's not the syntax, it's the concept. > By example, with this switch, that has two cases that are semantically > identical > ? switch(point) { > ??? case Point p where p.x() == 0 -> ... > ? ? case Point(var x, var _) where x == 0 -> ... No, they are not semantically identical, except maybe for a record (because records are so constrained.)? The first is invoking a method p.x(); the second is invoking a deconstructor, which has a binding called x.? If the two happen to be talking about the same x, then it will come out the same, but you have no reason to assume that just based on the spelling of `x`.? They could be describing entirely different things.? The language has no business guessing the semantics of a method based on its name. In order to make them work out, you need a system of "properties" to guarantee that when a deconstructor binds a `x`, and an accessor method returns an `x`, they are guaranteed to be the same `x`.? I don't blame you for wanting such a system, but you don't get to sneak it in the back door.... > How the deconstructors are represented in the surface language, how > they are called or how the data values flow to the bindings are > another stories for another time. No, this is not a syntax problem; it is a conceptual problem.? You are asserting that deconstructors means something different than they do. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 8 13:53:36 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 8 Sep 2020 09:53:36 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> Message-ID: > - Is either total or partial. Deconstruction patterns are total; other patterns we'll be able to express later, such as `Optional.of(var x)`, are partial. While I don?t want to get into a deep dive on declared patterns now, let?s just say that they?ll exist and they?re important (Optional.of(var x), Optional.empty(), etc.) So in increasing order of generality: Type patterns: T t Deconstruction patterns: T(var x) Declared patterns: Optional.of(var x) Remi would like to view deconstruction patterns as a generalization of type pattern, but they are more properly understood as a restriction of declared patterns ? just as constructors can be understood as a restriction of methods. Because we haven?t seen much of declared patterns yet, it is easier to miss their role in the spectrum, but their role is pretty obvious once you step back and think about it. Deconstruction patterns are constrained compared to declared patterns in: - their name (like constructors!) - They are total on their declaring class, so they are guaranteed to match (much like a constructor is guaranteed to not return null) - While instance members, they are not inherited (just like constructors) - They can only be called via a pattern match (just as a constructor can only be called via a `new` operation.) The role of a constructor is to take an external state description, validate and normalize it, and produce an instance. The role of a deconstructor is to take an instance and produce an external state description. (If the ctor has to copy mutable elements on the way in, the dtor is also likely to want to do the same on the way out.) In this way, both ctor and dtor mediate access between an external API and the internal representation. From brian.goetz at oracle.com Tue Sep 8 16:43:01 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 8 Sep 2020 12:43:01 -0400 Subject: Updated patterns-in-switch doc Message-ID: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> I have updated https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md based on our discussions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 9 17:29:40 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 9 Sep 2020 13:29:40 -0400 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: References: Message-ID: <0f0046a4-c33b-2231-d879-b7b00a4d22f8@oracle.com> Gavin points out another corner case here: when the LHS is the `null` literal: ??? if (null instanceof String s) { ... } Since this is also in the "stupid question" category, it is reasonable to outlaw it, at least when there's a pattern on the RHS. I wonder how often this actually occurs in real code.... On 8/26/2020 11:00 AM, Brian Goetz wrote: > > Proposed: An `instanceof` expression must be able to evaluate to both > true and false, otherwise it is invalid.? This rules out strongly > total patterns on the RHS.? If you have a strongly total pattern, use > pattern assignment instead. -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Thu Sep 10 20:03:31 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 10 Sep 2020 14:03:31 -0600 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: <0f0046a4-c33b-2231-d879-b7b00a4d22f8@oracle.com> References: <0f0046a4-c33b-2231-d879-b7b00a4d22f8@oracle.com> Message-ID: > On Sep 9, 2020, at 11:29 AM, Brian Goetz wrote: > > Gavin points out another corner case here: when the LHS is the `null` literal: > > if (null instanceof String s) { ... } > > Since this is also in the "stupid question" category, it is reasonable to outlaw it, at least when there's a pattern on the RHS. One thing to be careful of with "stupid question" errors is that sometimes stupid questions can be useful for experimentation. For example, it's nice that I can go to jshell and see what 'null instance String' does without getting my hand slapped. (I don't think there's a bright line between stupid questions that should be disallowed and those that should are useful. It's a judgement call.) From maurizio.cimadamore at oracle.com Thu Sep 10 20:38:14 2020 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 10 Sep 2020 21:38:14 +0100 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: References: Message-ID: On 26/08/2020 16:00, Brian Goetz wrote: > While I get why some people would like to bootstrap this into an > argument why the pattern semantics are wrong, the key observation here > is: _both of these questions are stupid_.? So I think there's an > obvious way to fix this so that there is no problem here: instanceof > must ask a question.? So the second form would be illegal, with a > compiler error saying "pattern always matches the target." > > Proposed: An `instanceof` expression must be able to evaluate to both > true and false, otherwise it is invalid.? This rules out strongly > total patterns on the RHS.? If you have a strongly total pattern, use > pattern assignment instead. I think it seems ok - and rather obvious when looking at the Object case. The issue is always gonna be with things like: x() instanceof Foo And then return type of x() changes from Object to something else, thus creating some effect at distance. But, we already have effect at distance of this kind in switch, for nullity analysis (right?), so it's not like this is adding something new (although it will create new source compatibility scenario for existing code). Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 10:28:30 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 13 Sep 2020 12:28:30 +0200 (CEST) Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: References: Message-ID: <988422009.1665742.1599992910439.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Gavin Bierman" > ?: "amber-spec-experts" > Envoy?: Lundi 27 Juillet 2020 12:53:52 > Objet: Finalizing in JDK 16 - Pattern matching for instanceof > In JDK 16 we are planning to finalize two JEPs: > > - Pattern matching for `instanceof` > - Records > > Whilst we don't have any major open issues for either of these features, I would > like us to close them out. So I thought it would be useful to quickly summarize > the features and the issues that have arisen over the preview periods so far. In > this email I will discuss pattern matching; a following email will cover the > Records feature. I think that Pattern matching for `instanceof` should stay in the preview feature state because as we discuss about how pattern matching works, there is a strong feeling that both features should be aligned, so i see Pattern matching for `instanceof` being promoted as a non-preview feature as an unnecessary risk until the pattern matching feature is not ready. R?mi From forax at univ-mlv.fr Sun Sep 13 10:35:55 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 13 Sep 2020 12:35:55 +0200 (CEST) Subject: Updated patterns-in-switch doc In-Reply-To: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> Message-ID: <1575939966.1667368.1599993355779.JavaMail.zimbra@u-pem.fr> At the end of the section "refining-totality" [1], The sentence "Guarded patterns should be ignored entirely for purposes of computing totality." implies that if two patterns that only differ from one having a where and the other have not it's not valif to have them both in a switch seems wrong for me. By example, a switch like this is illegal switch(foo) { case Bar bar where bar.x == 0 -> ... case Bar bar -> ... ... } I believe that what we want is to consider that a pattern with a where clause is considered as a "subtype" of the pattern without a where clause, whatever the where clause is exactly. R?mi [1] https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md#refining-totality > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Mardi 8 Septembre 2020 18:43:01 > Objet: Updated patterns-in-switch doc > I have updated > [ > https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md > | > https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md > ] > based on our discussions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 11:00:45 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 13:00:45 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> Message-ID: <366751516.1670542.1599994845523.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "Guy Steele" , "Alan Malloy" , > "amber-spec-experts" > Envoy?: Mardi 8 Septembre 2020 00:52:11 > Objet: Re: Is case var(var x, var y) a valid syntax ? >> The deconstruction pattern is a special case of type pattern > No, it isn't. I think you want it to be, but it isn't. >> , both starts by doing a typecheck (an instanceof), what is different is how >> they bind variables after the typecheck. >> So having the type inference on the type used by the typecheck working for the >> type pattern but not for the destruction pattern strike me as weird. > I think you may just not understand the model here. > In generality, a pattern: > - Is either total or partial. Deconstruction patterns are total; other patterns > we'll be able to express later, such as `Optional.of(var x)`, are partial. > - Has a target type. In order for the pattern to match, the dynamic type of the > target must be cast-convertible to this target type. > - Has a set of output bindings. Let's focus on the total pattern first, as you said a deconstruction pattern is total, it's an instanceof + a destructuring phase + a binding phase, while a type pattern is just an instanceof + a binding phase, that why a deconstruction pattern is a subset of a A declared pattern (the partial one) is an instanceof + destructurring + a where clause + a binding phase so it's not a subset of a deconstruction pattern but it's a subset of the type pattern too. > So, if the user says: > case Foo(int x): > This means: > - (static) perform overload selection on the deconstructors of Foo, and look for > one that is compatible with the binding list `(int x)`. Compile error if there > isn't one (or are too many.) > - (dynamic) test the target to see if it is cast-convertible to Foo. If it is, > because the pattern is total (no additional match conditions), the > deconstruction pattern is going to match. Invoke the deconstructor to get the > bindings. yes, it's the same semantics of a type pattern followed by a call to the deconstructor + a binding of the variables, i believe that the problem is that you see the deconstructor as an inverse of the constructor, which is a kind of right but not fully right. >> At the same time, i understand why people can find the syntax awkward. I just >> want to be sure that it's not awkward just because it's a new syntax. > It's not the syntax, it's the concept. >> By example, with this switch, that has two cases that are semantically identical >> switch(point) { >> case Point p where p.x() == 0 -> ... >> case Point(var x, var _) where x == 0 -> ... > No, they are not semantically identical, except maybe for a record (because > records are so constrained.) The first is invoking a method p.x(); the second > is invoking a deconstructor, which has a binding called x. If the two happen to > be talking about the same x, then it will come out the same, but you have no > reason to assume that just based on the spelling of `x`. They could be > describing entirely different things. The language has no business guessing the > semantics of a method based on its name. given your example here, it doesn't work for a record too, if someone add an override method x() that return the value of y, it doesn't work. The same is true with a deconstructor, if you return for the deconstructor (int x, int y) the value of y and 0, you have exacly the same kind of issue. So there are semantically equivalent iff the accessor and the deconstructor are not correctly written. > In order to make them work out, you need a system of "properties" to guarantee > that when a deconstructor binds a `x`, and an accessor method returns an `x`, > they are guaranteed to be the same `x`. I don't blame you for wanting such a > system, but you don't get to sneak it in the back door.... you don't need properties, because for a record, you have the components which is very like properties and for the other classes, you have a mechanism to transform an instance to a record, what you call a deconstructor, so by transitivity you have a way to unbundle an instance to a set of components. >> How the deconstructors are represented in the surface language, how they are >> called or how the data values flow to the bindings are another stories for >> another time. > No, this is not a syntax problem; it is a conceptual problem. You are asserting > that deconstructors means something different than they do. No, what i've proposed is _at runtime_ to have a different way to bind the components to the local variable to have a better backward compatibility, from the language POV, you still have a deconstructor that you call You have proposed to use a synthetic record as return value of a deconstructor, i don't like it because it means that each time you re-compile your code you may have an issue and that if you use a name generated from the set of all record component types, it doesn't work if you add a new component. I think that we can come with a better way to reference the deconstructor in the bytecode in a way that support adding a new component to a deconstructor. I may be wrong, but anyway, it has nothing to do with if a type pattern is a subtype of a deconstructor pattern or not. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 11:21:00 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 13:21:00 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> Message-ID: <166679132.1673062.1599996060565.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "Guy Steele" , "Alan Malloy" , "amber-spec-experts" > > Envoy?: Mardi 8 Septembre 2020 15:53:36 > Objet: Re: Is case var(var x, var y) a valid syntax ? >> - Is either total or partial. Deconstruction patterns are total; other patterns >> we'll be able to express later, such as `Optional.of(var x)`, are partial. > > While I don?t want to get into a deep dive on declared patterns now, let?s just > say that they?ll exist and they?re important (Optional.of(var x), > Optional.empty(), etc.) So in increasing order of generality: > > Type patterns: T t > Deconstruction patterns: T(var x) > Declared patterns: Optional.of(var x) > > Remi would like to view deconstruction patterns as a generalization of type > pattern, but they are more properly understood as a restriction of declared > patterns ? just as constructors can be understood as a restriction of methods. > Because we haven?t seen much of declared patterns yet, it is easier to miss > their role in the spectrum, but their role is pretty obvious once you step back > and think about it. see below > > Deconstruction patterns are constrained compared to declared patterns in: > > - their name (like constructors!) > - They are total on their declaring class, so they are guaranteed to match (much > like a constructor is guaranteed to not return null) > - While instance members, they are not inherited (just like constructors) At least you want a deconstructor to be overrideable (which is not fully equivalent to being inherited). A deconstructor is for allowing encapsulation so the world projected by a deconstructor may have a little to share with how the the class are implemented class Employee { int baseSalary; deconstructor Employee(int salary) { return (baseSalary); } } class VP extends Employee { int bonus; deconstructor VP(int salary) { return (baseSalary + bonus); } } ... Vp vp = new VP(); vp.setBaseSalary(2000); vp.setBonus(500); Employee employee = vp; employee instanceof Employee(salary) { System.out.println(salary); // 2500 } and here you can see that Employee(salary) is not a call to the deconstructor but an instanceof Employee + a call to the deconstructor (int salary) ! > - They can only be called via a pattern match (just as a constructor can only be > called via a `new` operation.) so unlike a constructor that can be called either by a new or by this(...) and super(...) a deconstructor can only be called vua pattern matching. > > The role of a constructor is to take an external state description, validate and > normalize it, and produce an instance. The role of a constructor is to hide the information of how a class is implemented. Only if all the fields are final it plays the role of the single gated entry point. > The role of a deconstructor is to take an instance and produce an external state > description. (If the ctor has to copy mutable elements on the way in, the dtor > is also likely to want to do the same on the way out.) The role of a deconstructor is to hide the information of how the class is implemented and allow to pattern match to a projected form of the instance. > > In this way, both ctor and dtor mediate access between an external API and the > internal representation. It's only true for a constructor if the constructor (constructors) are the only way to change the value of an instance. It's only true for a deconstructor if the deconstructor has a matching constructor I start to think that calling a deconstructor a deconstructor is not the right term because while a constructor and a deconstructor can be dual of each other, this mirroring in not true in the general case, it's only guarantee in the case of record because you have a canonical constructor. R?mi From forax at univ-mlv.fr Sun Sep 13 12:08:19 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 13 Sep 2020 14:08:19 +0200 (CEST) Subject: Updated patterns-in-switch doc In-Reply-To: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> Message-ID: <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> Section Do we need constant patterns, if we don't have constant pattern, it may also means that the type of a bound variables can be implicit too. i.e. case Point(x, y) having the same meaning as case Point(var x, var y) like with lambdas. Because int c = ... switch(getFoo()) { case Foo(c): ... } is not valid anymore and should be written like this int c = ... switch(getFoo()) { case Foo(x) where x == c: ... } R?mi > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Mardi 8 Septembre 2020 18:43:01 > Objet: Updated patterns-in-switch doc > I have updated > [ > https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md > | > https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md > ] > based on our discussions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 13:20:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 09:20:28 -0400 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: <988422009.1665742.1599992910439.JavaMail.zimbra@u-pem.fr> References: <988422009.1665742.1599992910439.JavaMail.zimbra@u-pem.fr> Message-ID: > I think that Pattern matching for `instanceof` should stay in the preview feature state because as we discuss about how pattern matching works, > there is a strong feeling that both features should be aligned, so i see Pattern matching for `instanceof` being promoted as a non-preview feature as an unnecessary risk until the pattern matching feature is not ready. There?s a lot of negatives in this phrasing, so can you turn it around and, instead of saying what we should not, do, say what you think we should? The logical conclusion of what you are suggesting is that we should not ship _any_ of pattern matching until _all_ of it is ready. But since this is a story that will play out for a long time, that means no one gets the benefit of this relatively simple form for years. Is that really what you are suggesting? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 13:42:39 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 09:42:39 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <166679132.1673062.1599996060565.JavaMail.zimbra@u-pem.fr> References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> <166679132.1673062.1599996060565.JavaMail.zimbra@u-pem.fr> Message-ID: <976B62BD-111B-4FE3-A668-03B73B132A32@oracle.com> >> - While instance members, they are not inherited (just like constructors) > > At least you want a deconstructor to be overrideable (which is not fully equivalent to being inherited). > A deconstructor is for allowing encapsulation so the world projected by a deconstructor may have a little to share with how the the class are implemented > > class Employee { > int baseSalary; > > deconstructor Employee(int salary) { return (baseSalary); } > } > class VP extends Employee { > int bonus; > > deconstructor VP(int salary) { return (baseSalary + bonus); } > } > > ... > Vp vp = new VP(); > vp.setBaseSalary(2000); > vp.setBonus(500); > Employee employee = vp; > employee instanceof Employee(salary) { > System.out.println(salary); // 2500 > } > > > and here you can see that Employee(salary) is not a call to the deconstructor but an instanceof Employee + a call to the deconstructor (int salary) ! I think what you are alluding to here is the idea that a deconstructor is like a ?multi-accessor?, and accessors are virtual but deconstructors are not. But the example is distorted for two reasons; this is already a questionable deconstructor API, and even if so, Employee is conflicted about the distinction between salary and baseSalary. So I?m not sure how much we can learn from this particular example. Maybe you have a better one? > > >> - They can only be called via a pattern match (just as a constructor can only be >> called via a `new` operation.) > > so unlike a constructor that can be called either by a new or by this(...) and super(...) a deconstructor can only be called vua pattern matching. You should re-read the document about deconstructors, as this symmetry is well covered. The case of one deconstructor delegating to another, just like one constructor delegating to another, is important, because we want each class to be responsible for its own state. So yes, this is covered. (Technically, though, this sort of delegated invocation _is_ a pattern match, so the statement ?only through pattern matching? still stands.) > >> In this way, both ctor and dtor mediate access between an external API and the >> internal representation. > > It's only true for a constructor if the constructor (constructors) are the only way to change the value of an instance. > It's only true for a deconstructor if the deconstructor has a matching constructor Both of these ?only true? claims are not true :) You can have multiple constructors with different views of the state (overloading), and these views could be overlapping or non-overlapping. And you can have multiple deconstructors with different views of the state too. And have the choice to align the constructor / deconstructor views, or not. You can have matching ctor/dtor, or asymmetric ones ? this is a matter of API design. We anticipate it will be common to provide matched ctor/dtor pairs, because together these form an adjunction between the state space of the object and the external API shared between ctor/dtor, which is a useful and practical building block. Even for mutable objects, deconstructors are still sensible. class C { int x; C(int x) { this.x = x; } deconstructor C(int x) { x = this.x; } void setX(int x) { this.x = x; } } I can do: C c = new C(3); c.setX(4); if (c instanceof C(var x)) { ? x is 4 here ? } The deconstructor is free to track the state, mutable or not. Again, this is a tool for API design, and it can be used in multiple ways. The record case is notable because records have a highly constrained API and they are not extensible, so they are the best behaved of the bunch. But classes can play this game too. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 13:47:26 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 09:47:26 -0400 Subject: Updated patterns-in-switch doc In-Reply-To: <1575939966.1667368.1599993355779.JavaMail.zimbra@u-pem.fr> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1575939966.1667368.1599993355779.JavaMail.zimbra@u-pem.fr> Message-ID: > The sentence "Guarded patterns should be ignored entirely for purposes of computing totality." implies that if two patterns that only differ from one having a where and the other have not it's not valif to have them both in a switch seems wrong for me. I suspect you have drawn the wrong implication from this statement. > By example, a switch like this is illegal > switch(foo) { > case Bar bar where bar.x == 0 -> ... > case Bar bar -> ... > ... > } This switch is perfectly legal and in fact we expect this pattern to be extremely common; we catch the special Bars first, and then have a catch-all for the rest of the Bars. The statement ?ignored for purposes of computing totality? means that the *first* `case Bar where?` does not play into the computation of whether the set of patterns is total on the target of the switch. But the second `case Bar` does, because it is not guarded. > I believe that what we want is to consider that a pattern with a where clause is considered as a "subtype" of the pattern without a where clause, whatever the where clause is exactly. That is the intuition; `case P where e` only covers some subset of the elements matched by P. We just can?t conclude anything about totality from a guarded pattern. We are not going to try to claim that case Foo f where f.x() == 3: case Foo f where f.x() != 3: is total on Foo; this is a fool?s errand. So the logical thing to do is say ?all bets regarding totality are off? for a particular case with a guard. Because the pattern you write is expected to be common, there will often be an unguarded version of the same pattern coming soon anyway, and it can play its role in totality computation. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 13:53:18 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 09:53:18 -0400 Subject: Updated patterns-in-switch doc In-Reply-To: <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> Message-ID: <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> > if we don't have constant pattern, it may also means that the type of a bound variables can be implicit too. > i.e. case Point(x, y) having the same meaning as case Point(var x, var y) like with lambdas. I don?t quite get the leap from ?no constant patterns? to changing the syntax of deconstruction patterns, but in any case, we definitely don?t want this (and in fact, for the same reasons cited in the section on constant patterns, and others.) I don?t want people wondering whether `foo(x)` is an invocation of a method or a pattern. One of the problems with the obvious denotation of constant patterns is that `foo(0)` looks like an invocation with zero. With a variable, it is worse. Further, allowing `Point(x,y)` as a pattern would allow variables to be declared without anything that even looks like a variable declaration! Many people are already disturbed by the idea that variables can now be declared from within expressions; without an `int x` or `var x` to at least have a declaration to point to, it will be untenable. > is not valid anymore and should be written like this > int c = ... > switch(getFoo()) { > case Foo(x) where x == c: ... > } No, it should be written case Foo(var x) where x == c: -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 17:49:17 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 19:49:17 +0200 (CEST) Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: References: <988422009.1665742.1599992910439.JavaMail.zimbra@u-pem.fr> Message-ID: <1677794867.1740105.1600019357996.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "Gavin Bierman" , "amber-spec-experts" > > Envoy?: Dimanche 13 Septembre 2020 15:20:28 > Objet: Re: Finalizing in JDK 16 - Pattern matching for instanceof >> I think that Pattern matching for `instanceof` should stay in the preview >> feature state because as we discuss about how pattern matching works, >> there is a strong feeling that both features should be aligned, so i see Pattern >> matching for `instanceof` being promoted as a non-preview feature as an >> unnecessary risk until the pattern matching feature is not ready. > There?s a lot of negatives in this phrasing, so can you turn it around and, > instead of saying what we should not, do, say what you think we should? The > logical conclusion of what you are suggesting is that we should not ship _any_ > of pattern matching until _all_ of it is ready. I think we should release features in sync, by example, instanceof T t and switch with the type patterns are the very similar so they should both have the same preview status. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 18:20:15 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 20:20:15 +0200 (CEST) Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <976B62BD-111B-4FE3-A668-03B73B132A32@oracle.com> References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> <166679132.1673062.1599996060565.JavaMail.zimbra@u-pem.fr> <976B62BD-111B-4FE3-A668-03B73B132A32@oracle.com> Message-ID: <520772990.1745863.1600021215119.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Dimanche 13 Septembre 2020 15:42:39 > Objet: Re: Is case var(var x, var y) a valid syntax ? >>> - While instance members, they are not inherited (just like constructors) >> At least you want a deconstructor to be overrideable (which is not fully >> equivalent to being inherited). >> A deconstructor is for allowing encapsulation so the world projected by a >> deconstructor may have a little to share with how the the class are implemented >> class Employee { >> int baseSalary; >> deconstructor Employee(int salary) { return (baseSalary); } >> } >> class VP extends Employee { >> int bonus; >> deconstructor VP(int salary) { return (baseSalary + bonus); } >> } >> ... >> Vp vp = new VP(); >> vp.setBaseSalary(2000); >> vp.setBonus(500); >> Employee employee = vp; >> if (employee instanceof Employee(salary) { >> System.out.println(salary); // 2500 >> } >> and here you can see that Employee(salary) is not a call to the deconstructor >> but an instanceof Employee + a call to the deconstructor (int salary) ! > I think what you are alluding to here is the idea that a deconstructor is like a > ?multi-accessor?, and accessors are virtual but deconstructors are not. > But the example is distorted for two reasons; this is already a questionable > deconstructor API, and even if so, Employee is conflicted about the distinction > between salary and baseSalary. if deconstructors are not virtual, Is the salary printed is 2000 or the compiler raises an error somewhere ? > So I?m not sure how much we can learn from this particular example. Maybe you > have a better one? No a better one, just another one, you want to be able to declare a deconstructor abstract by example on an interface interface Map { interface Entry { public abstract deconstructor Entry(K key, V value); } } ... for(Map.Entry(var key, var value) : entries) { ... } BTW, it's also an example where 'var' can be useful instead of having to specify the full type for(var(var key, var value) : entries) { ... } >>> - They can only be called via a pattern match (just as a constructor can only be >>> called via a `new` operation.) >> so unlike a constructor that can be called either by a new or by this(...) and >> super(...) a deconstructor can only be called via pattern matching. > You should re-read the document about deconstructors, as this symmetry is well > covered. The case of one deconstructor delegating to another, just like one > constructor delegating to another, is important, because we want each class to > be responsible for its own state. So yes, this is covered. (Technically, > though, this sort of delegated invocation _is_ a pattern match, so the > statement ?only through pattern matching? still stands.) I get that you can write the code like this if baseSalary is declared private in Employee class VP extends Employee { int bonus; deconstructor VP(int salary) { super(var baseSalary) = this; return (baseSalary + bonus); } } still, a deconstructor is unlike a constructor because you can call a constructor directly something you can not do with a deconstructor. >>> In this way, both ctor and dtor mediate access between an external API and the >>> internal representation. >> It's only true for a constructor if the constructor (constructors) are the only >> way to change the value of an instance. >> It's only true for a deconstructor if the deconstructor has a matching >> constructor > Both of these ?only true? claims are not true :) > You can have multiple constructors with different views of the state > (overloading), and these views could be overlapping or non-overlapping. And you > can have multiple deconstructors with different views of the state too. And > have the choice to align the constructor / deconstructor views, or not. You can > have matching ctor/dtor, or asymmetric ones ? this is a matter of API design. so why my first example is questionable usage of the deconstructor API. > We anticipate it will be common to provide matched ctor/dtor pairs, because > together these form an adjunction between the state space of the object and the > external API shared between ctor/dtor, which is a useful and practical building > block. > Even for mutable objects, deconstructors are still sensible. > class C { > int x; > C(int x) { this.x = x; } > deconstructor C(int x) { x = this.x; } > void setX(int x) { this.x = x; } > } > I can do: > C c = new C(3); > c.setX(4); > if (c instanceof C(var x)) { ? x is 4 here ? } > The deconstructor is free to track the state, mutable or not. Again, this is a > tool for API design, and it can be used in multiple ways. The record case is > notable because records have a highly constrained API and they are not > extensible, so they are the best behaved of the bunch. But classes can play > this game too. and now introduce inheritance to the mix and you will see that a deconstructor has to be virtual. Basically, we are at the point where it's not useful anymore to call it a deconstructor because while it's like the reverse of a constructor, at the same time it's also close to an API like clone(). R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 18:30:34 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 20:30:34 +0200 (CEST) Subject: Updated patterns-in-switch doc In-Reply-To: <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> Message-ID: <1164333907.1747745.1600021834250.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Dimanche 13 Septembre 2020 15:53:18 > Objet: Re: Updated patterns-in-switch doc >> if we don't have constant pattern, it may also means that the type of a bound >> variables can be implicit too. >> i.e. case Point(x, y) having the same meaning as case Point(var x, var y) like >> with lambdas. > I don?t quite get the leap from ?no constant patterns? to changing the syntax of > deconstruction patterns, but in any case, we definitely don?t want this (and in > fact, for the same reasons cited in the section on constant patterns, and > others.) if case Point(x, y) can not mean instanceof Point p where p.x == x && p.y == y, then case Point(x, y) can have the same meaning has a lambda (x, y), introducing two fresh variables x and y. > I don?t want people wondering whether `foo(x)` is an invocation of a method or a > pattern. One of the problems with the obvious denotation of constant patterns > is that `foo(0)` looks like an invocation with zero. With a variable, it is > worse. > Further, allowing `Point(x,y)` as a pattern would allow variables to be declared > without anything that even looks like a variable declaration! Many people are > already disturbed by the idea that variables can now be declared from within > expressions; without an `int x` or `var x` to at least have a declaration to > point to, it will be untenable. Do you think that the way we declare lambdas is untenable ? Because with a lambda, we are introducing fresh variables without an explicit type or a var, you can use an explicit type or a var but you don't have to, and while i agree that the lambda syntax takes a little time to adjust when you are not familiar with it, i don't see people still struggling with that syntax in the long term. >> is not valid anymore and should be written like this >> int c = ... >> switch(getFoo()) { >> case Foo(x) where x == c: ... >> } > No, it should be written > case Foo(var x) where x == c: see above. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 18:30:45 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 14:30:45 -0400 Subject: Is case var(var x, var y) a valid syntax ? In-Reply-To: <520772990.1745863.1600021215119.JavaMail.zimbra@u-pem.fr> References: <1810367288.916993.1599498207934.JavaMail.zimbra@u-pem.fr> <1162477749.995924.1599515765693.JavaMail.zimbra@u-pem.fr> <166679132.1673062.1599996060565.JavaMail.zimbra@u-pem.fr> <976B62BD-111B-4FE3-A668-03B73B132A32@oracle.com> <520772990.1745863.1600021215119.JavaMail.zimbra@u-pem.fr> Message-ID: > > > ?So I?m not sure how much we can learn from this particular > example. ?Maybe you have a better one? > > > No a better one, just another one, you want to be able to declare a > deconstructor abstract by example on an interface > interface Map { > ? interface Entry { > ??? public abstract deconstructor Entry(K key, V value); > ? } > } Yes, valid goal, but now you're getting ahead of the story.? There is a plan for the "instance method analogue" of pattern declarations, but it's not called a "deconstructor", any more than a method is not called a constructor. The basic story is: ?- patterns are declared as members ?- some patterns are static (Optional.of(var x)), some are instance (Map.contains(k, var value)), and some are the same weird mix that constructors are (deconstructors) ?- Deconstructors are total (they deconstruct only), but the others are partial (they ask a question) Right now, we're looking only at the simplest kind of declared pattern -- deconstructors -- and building from there. > ... > for(Map.Entry(var key, var value) : entries) { > ? ... > } > > BTW, it's also an example where 'var' can be useful instead of having > to specify the full type > for(var(var key, var value) : entries) { > ? ... > } > > > I get that you can write the code like this if baseSalary is declared > private in Employee > > class VP extends Employee { > ???int bonus; > > ???deconstructor VP(int salary) { > ???? super(var baseSalary) = this; > ???? return (baseSalary + bonus); > ? } > ?} > > still, a deconstructor is unlike a constructor because you can call a > constructor directly something you can not do with a deconstructor. You cannot call a constructor directly either!? You execute a "new" operation, which has multiple consequences, only one of which is calling the constructor to fill in the state of the object.? The same is true with deconstructors -- you do a pattern match, which, under the right circustances, causes the deconstructor to be called. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 18:34:54 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 14:34:54 -0400 Subject: Updated patterns-in-switch doc In-Reply-To: <1164333907.1747745.1600021834250.JavaMail.zimbra@u-pem.fr> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> <1164333907.1747745.1600021834250.JavaMail.zimbra@u-pem.fr> Message-ID: <9f3f9e3d-bb38-9eba-df92-77ee8d036580@oracle.com> > I don?t quite get the leap from ?no constant patterns? to changing > the syntax of deconstruction patterns, but in any case, we > definitely don?t want this (and in fact, for the same reasons > cited in the section on constant patterns, and others.) > > > if case Point(x, y) can not mean instanceof Point p where p.x == x && > p.y == y, then case Point(x, y) can have the same meaning has a lambda > (x, y), introducing two fresh variables x and y. If you're making the claim that "It would not be disastrously inconsistent for it to work that way", I agree.? But I think it would still be quite foolish of us to go that way anyway.? The benefit is tiny (a few fewer characters typed) at a very considerable cost -- reduced readability, and potential ambiguities. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 13 19:10:44 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 13 Sep 2020 21:10:44 +0200 (CEST) Subject: Updated patterns-in-switch doc In-Reply-To: <9f3f9e3d-bb38-9eba-df92-77ee8d036580@oracle.com> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> <1164333907.1747745.1600021834250.JavaMail.zimbra@u-pem.fr> <9f3f9e3d-bb38-9eba-df92-77ee8d036580@oracle.com> Message-ID: <266560646.1753475.1600024244957.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Dimanche 13 Septembre 2020 20:34:54 > Objet: Re: Updated patterns-in-switch doc >>> I don?t quite get the leap from ?no constant patterns? to changing the syntax of >>> deconstruction patterns, but in any case, we definitely don?t want this (and in >>> fact, for the same reasons cited in the section on constant patterns, and >>> others.) >> if case Point(x, y) can not mean instanceof Point p where p.x == x && p.y == y, >> then case Point(x, y) can have the same meaning has a lambda (x, y), >> introducing two fresh variables x and y. > If you're making the claim that "It would not be disastrously inconsistent for > it to work that way", I agree. But I think it would still be quite foolish of > us to go that way anyway. The benefit is tiny (a few fewer characters typed) at > a very considerable cost -- reduced readability, and potential ambiguities. The main issue i see with using var is that when you have nested patterns, "var" starts to becomes noise because you start to have several of them, so it makes the nesting less obvious. For me, var is good the first times to see how pattern matching works, it's very similar how as a beginner you are prefixing everything with "this" but after some times, it falls into the noise category, that why i'm not proposing to not allow "var" but to allow to not use "var". Again, this is very similar to the things we were discussing during the conception of lambdas, should we try to protect them with a lot of syntax to say, this is the new thing or should we try to make the syntax simpler with the risk of people not understanding it. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Sep 13 22:23:33 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Sep 2020 18:23:33 -0400 Subject: Updated patterns-in-switch doc In-Reply-To: <266560646.1753475.1600024244957.JavaMail.zimbra@u-pem.fr> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> <1615713741.1677939.1599998899059.JavaMail.zimbra@u-pem.fr> <5A2781B2-6DEA-4B07-8D32-0A98ACEAD837@oracle.com> <1164333907.1747745.1600021834250.JavaMail.zimbra@u-pem.fr> <9f3f9e3d-bb38-9eba-df92-77ee8d036580@oracle.com> <266560646.1753475.1600024244957.JavaMail.zimbra@u-pem.fr> Message-ID: <84c7e756-9d7a-5ac9-1bf8-fa627b1ec482@oracle.com> I find these arguments not-remotely-compelling. Variable declarations should look like declarations; it is highly error-prone to have the mere mention of a name cause it to spring into being.? I see no value, and much anti-value, in having `Point(x)` introduce a new variable x. So, no.? I think that would be a very bad idea. On 9/13/2020 3:10 PM, forax at univ-mlv.fr wrote: > > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"Remi Forax" > *Cc: *"amber-spec-experts" > *Envoy?: *Dimanche 13 Septembre 2020 20:34:54 > *Objet: *Re: Updated patterns-in-switch doc > > > I don?t quite get the leap from ?no constant patterns? to > changing the syntax of deconstruction patterns, but in any > case, we definitely don?t want this (and in fact, for the > same reasons cited in the section on constant patterns, > and others.) > > > if case Point(x, y) can not mean instanceof Point p where p.x > == x && p.y == y, then case Point(x, y) can have the same > meaning has a lambda (x, y), introducing two fresh variables x > and y. > > > If you're making the claim that "It would not be disastrously > inconsistent for it to work that way", I agree.? But I think it > would still be quite foolish of us to go that way anyway.? The > benefit is tiny (a few fewer characters typed) at a very > considerable cost -- reduced readability, and potential ambiguities. > > > The main issue i see with using var is that when you have nested > patterns, "var" starts to becomes noise because you start to have > several of them, so it makes the nesting less obvious. > For me, var is good the first times to see how pattern matching works, > it's very similar how as a beginner you are prefixing everything with > "this" but after some times, it falls into the noise category, > that why i'm not proposing to not allow "var" but to allow to not use > "var". > Again, this is very similar to the things we were discussing during > the conception of lambdas, should we try to protect them with a lot of > syntax to say, this is the new thing or should we try to make the > syntax simpler with the risk of people not understanding it. > > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Fri Sep 18 13:47:36 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 18 Sep 2020 14:47:36 +0100 Subject: [records] Is C-style array declaration in components dropped? In-Reply-To: References: Message-ID: Hi Tagir, [Just tidying up the next version of the spec?and going through my list of outstanding emails.] > On 24 Jul 2020, at 05:06, Tagir Valeev wrote: > > Btw if this change is intended, then it looks like the changes in > section 10.2 [1] must be dropped as well > > The array type of a variable depends on the bracket pairs that may > appear as part of the type at the beginning of a variable declaration, > or as part of the declarator for the variable, or both. Specifically, > in the declaration of a field, formal parameter, local variable, or > record component (8.3, 8.4.1, 9.3, 9.4, 14.4.1, 14.14.2, 15.27.1, > 8.10.1), the array type of the variable is denoted by <...> > > Now, this text is inconsistent with 8.10.1. Actually it isn?t inconsistent as we need this text to allow for variable arity record component declarations. What is needed is to keep this as it is, but additionally to amend the three bullet points that follow to: - the element type that appears at the beginning of the declaration; then, - any bracket pairs that follow the variable's Identifier in the declarator (not applicable for a variable arity parameter **or variable arity record component**); then, - any bracket pairs that appear in the type at the beginning of the declaration (where the ellipsis of a variable arity parameter **or variable arity record component** is treated as a bracket pair). (Text marked **?** is new). Thanks, Gavin From maurizio.cimadamore at oracle.com Fri Sep 18 15:23:35 2020 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 18 Sep 2020 16:23:35 +0100 Subject: [records] Is C-style array declaration in components dropped? In-Reply-To: <29901fcd-8dc5-55d6-466e-4f58903865eb@oracle.com> References: <29901fcd-8dc5-55d6-466e-4f58903865eb@oracle.com> Message-ID: +1 on this - as Brian says - part of a long-term battle against old syntax - note that at some point in Java 7 we dropped support for C-style array syntax on varargs decl - e.g.: void m(int n, String... args[]) See: https://bugs.openjdk.java.net/browse/JDK-6569633 Maurizio On 24/07/2020 13:43, Brian Goetz wrote: > As we've added new places or ways to declare variables (e.g., var, > record components), we've been trying to not propagate this syntax > mistake.? So I agree that this is the right behavior; I think the > change just got lost in the shuffle. > > On 7/23/2020 11:55 PM, Tagir Valeev wrote: >> Hello! >> >> The JLS 14 record preview spec allows C-style array declaration in >> components (like record R(int x[])) [1]: >> >> RecordComponent: >> { VariableModifier } UnannType VariableDeclaratorId >> VariableArityRecordComponent >> VariableDeclaratorId: >> Identifier [ Dims ] >> >> However, it appears that JLS 15 draft spec doesn't allow it anymore [2] >> >> RecordComponent: >> { Annotation } UnannType Identifier >> VariableArityRecordComponent >> >> This change is not listed in the draft spec prolog in "The changes are >> the same as those in the first preview of Records in Java SE 14, >> except for the following", so I overlooked it when updated the records >> support in IntelliJ IDEA. Is this intended change? If yes, then >> probably the changes section should be updated to include it as well. >> >> I must say that I heavily support this change. Supporting C-style >> array declaration in records adds complexity in many places of our >> codebase and introduces subtle bugs. It's somewhat depressing to fix >> all of them, knowing that nobody would use this anyway. >> >> With best regards, >> Tagir Valeev. >> >> [1]https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10.1 >> [2]http://cr.openjdk.java.net/~gbierman/jep384/jep384-20200506/specs/records-jls.html#jls-8.10.1 > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Sep 18 15:35:46 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 18 Sep 2020 11:35:46 -0400 Subject: [records] Is C-style array declaration in components dropped? In-Reply-To: References: <29901fcd-8dc5-55d6-466e-4f58903865eb@oracle.com> Message-ID: <9150efa6-480a-825a-4a17-9fd6be1e044a@oracle.com> We also had to carve this out in LVTI; you can't say ??? var x[] and have us infer the component type. (I'd be happy to see this syntax deprecated, but just doesn't seem worth it.) On 9/18/2020 11:23 AM, Maurizio Cimadamore wrote: > > +1 on this - as Brian says - part of a long-term battle against old > syntax - note that at some point in Java 7 we dropped support for > C-style array syntax on varargs decl - e.g.: > > void m(int n, String... args[]) > > See: > https://bugs.openjdk.java.net/browse/JDK-6569633 > > Maurizio > > On 24/07/2020 13:43, Brian Goetz wrote: >> As we've added new places or ways to declare variables (e.g., var, >> record components), we've been trying to not propagate this syntax >> mistake.? So I agree that this is the right behavior; I think the >> change just got lost in the shuffle. >> >> On 7/23/2020 11:55 PM, Tagir Valeev wrote: >>> Hello! >>> >>> The JLS 14 record preview spec allows C-style array declaration in >>> components (like record R(int x[])) [1]: >>> >>> RecordComponent: >>> { VariableModifier } UnannType VariableDeclaratorId >>> VariableArityRecordComponent >>> VariableDeclaratorId: >>> Identifier [ Dims ] >>> >>> However, it appears that JLS 15 draft spec doesn't allow it anymore [2] >>> >>> RecordComponent: >>> { Annotation } UnannType Identifier >>> VariableArityRecordComponent >>> >>> This change is not listed in the draft spec prolog in "The changes are >>> the same as those in the first preview of Records in Java SE 14, >>> except for the following", so I overlooked it when updated the records >>> support in IntelliJ IDEA. Is this intended change? If yes, then >>> probably the changes section should be updated to include it as well. >>> >>> I must say that I heavily support this change. Supporting C-style >>> array declaration in records adds complexity in many places of our >>> codebase and introduces subtle bugs. It's somewhat depressing to fix >>> all of them, knowing that nobody would use this anyway. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> [1]https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10.1 >>> [2]http://cr.openjdk.java.net/~gbierman/jep384/jep384-20200506/specs/records-jls.html#jls-8.10.1 >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Sep 18 15:38:11 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 18 Sep 2020 17:38:11 +0200 (CEST) Subject: [records] Is C-style array declaration in components dropped? In-Reply-To: References: <29901fcd-8dc5-55d6-466e-4f58903865eb@oracle.com> Message-ID: <570609597.1350608.1600443491790.JavaMail.zimbra@u-pem.fr> > De: "Maurizio Cimadamore" > ?: "Brian Goetz" , "Tagir Valeev" , > "amber-spec-experts" > Envoy?: Vendredi 18 Septembre 2020 17:23:35 > Objet: Re: [records] Is C-style array declaration in components dropped? > +1 on this - as Brian says - part of a long-term battle against old syntax - > note that at some point in Java 7 we dropped support for C-style array syntax > on varargs decl - e.g.: > void m(int n, String... args[]) > See: > [ https://bugs.openjdk.java.net/browse/JDK-6569633 | > https://bugs.openjdk.java.net/browse/JDK-6569633 ] > Maurizio The real offender is still at large :) class Foo { String f() [] { return null; } } R?mi > On 24/07/2020 13:43, Brian Goetz wrote: >> As we've added new places or ways to declare variables (e.g., var, record >> components), we've been trying to not propagate this syntax mistake. So I agree >> that this is the right behavior; I think the change just got lost in the >> shuffle. >> On 7/23/2020 11:55 PM, Tagir Valeev wrote: >>> Hello! >>> The JLS 14 record preview spec allows C-style array declaration in >>> components (like record R(int x[])) [1]: >>> RecordComponent: >>> { VariableModifier } UnannType VariableDeclaratorId >>> VariableArityRecordComponent >>> VariableDeclaratorId: >>> Identifier [ Dims ] >>> However, it appears that JLS 15 draft spec doesn't allow it anymore [2] >>> RecordComponent: >>> { Annotation } UnannType Identifier >>> VariableArityRecordComponent >>> This change is not listed in the draft spec prolog in "The changes are >>> the same as those in the first preview of Records in Java SE 14, >>> except for the following", so I overlooked it when updated the records >>> support in IntelliJ IDEA. Is this intended change? If yes, then >>> probably the changes section should be updated to include it as well. >>> I must say that I heavily support this change. Supporting C-style >>> array declaration in records adds complexity in many places of our >>> codebase and introduces subtle bugs. It's somewhat depressing to fix >>> all of them, knowing that nobody would use this anyway. >>> With best regards, >>> Tagir Valeev. >>> [1] [ >>> https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10.1 >>> | >>> https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10.1 >>> ] [2] [ >>> http://cr.openjdk.java.net/~gbierman/jep384/jep384-20200506/specs/records-jls.html#jls-8.10.1 >>> | >>> http://cr.openjdk.java.net/~gbierman/jep384/jep384-20200506/specs/records-jls.html#jls-8.10.1 >>> ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Fri Sep 18 15:38:36 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 18 Sep 2020 16:38:36 +0100 Subject: [records] Specify use of @Override and @SafeVarargs on record components In-Reply-To: References: Message-ID: <023F93B1-0B21-4846-AFAE-AC97CB4C430A@oracle.com> Hi Tagir, Returning to this one: > On 22 Jan 2020, at 08:03, Tagir Valeev wrote: > > Hello! > > It appears that it's possible to annotate record component as > @Override or @SafeVarargs, and spec doesn't explicitly forbid this: > > record Test(@SafeVarargs int x, @Override int y) {} > > In these cases, it's assumed that the annotation propagates to the > accessor method. However, having @SafeVarargs on the accessor method > is prohibited, so should be in a record component. So I expect to see > in 9.6.4.7 something like this: > >> It is a compile-time error if a record component (?8.10.1) is annotated with the annotation @SafeVarargs. > > Having the @Override is possible on the accessor if accessor overrides > the interface method (and it's really cool possibility!) Probably it's > ok to allow this annotation on the record component in this case, but > it should be definitely disallowed if the accessor doesn't override > anything. So I expect something like this in 9.6.4.4: > >> If a record component (?8.10.1) of record R is annotated with the annotation @Override and the accessor method for that component (?8.10.3) does not override a method declared in a superinterface of R (?8.4.8.1), then a compile-time error occurs. I?d like to make sure both of these are covered as erroneous, but I?m not so keen on the suggested JLS text. Both of these cases are actually the same, i.e. the annotation on the implicitly declared method doesn?t make sense. So, I?d prefer to call that out explicitly as a single, catch-all rule. More precisely, in the draft spec in ?8.10.3 on implicitly declared accessor methods is the following bullet-point: - It is annotated with the annotations, if any, that appear on the corresponding record component and whose annotation types are applicable in the method declaration context, or in type contexts, or both. I propose to amend this to the following: - It is annotated with the annotations, if any, that appear on the corresponding record component and whose annotation types are applicable in the method declaration context, or in type contexts, or both. **The rules for these annotation modifiers, if any, on the accessor method are the same as for a method declaration, and are specified in [9.7.4] and [9.7.5].** The last sentence is new, and mirrors what is said about normal method declarations in ?8.4.3. What do you think? Gavin From eaftan at google.com Fri Sep 18 22:19:03 2020 From: eaftan at google.com (Eddie Aftandilian) Date: Fri, 18 Sep 2020 15:19:03 -0700 Subject: What does "total" really mean? Message-ID: Regard Brian's recent update to the type patterns in switch proposal ( https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md), I had some questions regarding totality. I really like the new presentation of this in terms of totality. It helps make clear why certain design decisions fall out of the basic principles behind type patterns in switches. However, I'm also a bit concerned about what "total" really means in this context. The term "total" calls to mind a total function, which is a function defined for all input values. But in this case, there's a carve out -- "total with remainder." The idea of "total with remainder" makes me uneasy, since if there is a remainder, by definition the function (or switch) is not total. It's also not clear to me how to characterize which values are "silly" and allowed to be in that remainder. "Silly" values are intended to be those that the programmer shouldn't have to care about in the common case, but that's not very satisfying as a principle. Finally, what are the limitations of how we determine totality? For example, consider a non-sealed abstract class A with a single concrete implementation C. The pattern "C c" should in principle be total on A, but practically with separate compilation it is impossible to determine that, so we add the constraint that A must be sealed for the totality analysis to work. Another example might be guards. Consider two guarded cases where the booleans are simply inverted. It is clear to a human (or possibly a static analyzer) that the switch is total, but it's probably not practical to determine that in general. A thought question: if you think of totality analysis as a type of static analysis, is the proposed analysis sound and/or complete? -Eddie -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Sep 18 22:32:45 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 18 Sep 2020 18:32:45 -0400 Subject: What does "total" really mean? In-Reply-To: References: Message-ID: <4dd27f07-391a-2ff8-072b-51897fc31f6b@oracle.com> Thanks Eddie.? Indeed, I think these could be clarified. We should start by putting out the motivation for why the "totality checker" is satisfied with less than full coverage -- this is because we want to be nice, and not pedantically ask users to write case clauses for "silly" cases that probably won't ever come up.? These sometimes include nulls (when deconstruction patterns are involved) and values from the future (when sealing or enums are involved), as well as closing over these with combinators (so Box(null) is part of the remainder for Box>.)? This should motivate _where_ the totality rules come from, and put claims of "silly values" on a more concrete foundation. Secondarily, perhaps totality isn't the right term; maybe we need a word for "good enough to satisfy the checker", where the checker is generous in letting us be sloppy regarding silly values. To your last point, indeed our ability to gauge totality is a best-efforts thing.? If there is a class A with only an implementation C, we can't deduce that without examining every classfile in the world -- unless A is sealed.? Similarly, we can't be expected to deem ??? case Foo f when bar() > 0: ??? case Foo f when bar() <= 0: as total on Foo; while it might be possible to solve the most trivial cases, this eventually heads towards a wrestling match with computational physics.? So the best way to win that game is not to play.? Here, the user has the option to refactor to: ??? case Foo f: ??????? if (bar() > 0) { ... } ??????? else { ... } which is more scrutable to the totality checker.? Indeed, even if we could tell that the two guards covered all values of `bar()`, this is an analysis better suited to a pure language. (Can we even get away with calling `bar()` only once?? I doubt it.? Writing side-effect-ful/impure guards might be questionable, but we probably have to assume people will do it anyway.) On 9/18/2020 6:19 PM, Eddie Aftandilian wrote: > Regard Brian's recent update to the type patterns in switch proposal > (https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md), > I had some questions regarding totality. > > I really like the new presentation of this in terms of totality.? It > helps make clear why certain design decisions fall out of the basic > principles behind type patterns in switches.? However, I'm also a bit > concerned about what "total" really means in this context. > > The term "total" calls to mind a total function, which is a function > defined for all input values.? But in this case, there's a carve out > -- "total with remainder."? The idea of "total with remainder" makes > me uneasy, since if there is a remainder, by definition the function > (or switch) is not total. > > It's also not clear to me how to characterize which values are "silly" > and allowed to be in that remainder.? "Silly" values are intended to > be those that the programmer shouldn't have to care about in the > common case, but that's not very satisfying as a principle. > > Finally, what are the limitations of how we determine totality?? For > example, consider a non-sealed abstract class A with a single concrete > implementation C.? The pattern "C c" should in principle be total on > A, but practically with separate compilation it is impossible to > determine that, so we add the constraint that A must be sealed for the > totality analysis to work.? Another example might be guards.? Consider > two guarded cases where the booleans are simply inverted.? It is clear > to a human (or possibly a static analyzer) that the switch is total, > but it's probably not practical to determine that in general. > > A thought question: if you think of totality analysis as a type of > static analysis, is the proposed analysis sound and/or complete? > > -Eddie > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Sep 19 09:23:09 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 19 Sep 2020 16:23:09 +0700 Subject: [records] Specify use of @Override and @SafeVarargs on record components In-Reply-To: <023F93B1-0B21-4846-AFAE-AC97CB4C430A@oracle.com> References: <023F93B1-0B21-4846-AFAE-AC97CB4C430A@oracle.com> Message-ID: Hello! On Fri, Sep 18, 2020 at 10:40 PM Gavin Bierman wrote: > - It is annotated with the annotations, if any, that appear on the corresponding > record component and whose annotation types are applicable in the method > declaration context, or in type contexts, or both. **The rules for these > annotation modifiers, if any, on the accessor method are the same as for a > method declaration, and are specified in [9.7.4] and [9.7.5].** > > The last sentence is new, and mirrors what is said about normal method declarations in ?8.4.3. > > What do you think? I'm not very good in English but I'm fine if this statement unambiguously imply that if the annotation is not applicable to the method, then this is a compilation error to put it on the record accessor. Btw, we allowed Override on explicit accessors, so now it might be unclear whether it's allowed on implicit ones. Also what if we mark the record component with `@SafeVarargs` and define an explicit accessor without this annotation? Like: record X(@SafeVarargs int y) { @Override public int y() { return y; } } Is this acceptable? Javac 15 happily compiles this. This brings more general question: is it allowed to put an annotation that targeted to METHOD only if we define explicit accessor? In this case, the annotation is not propagated anywhere and completely disappears from the bytecode, even if it has CLASS/RUNTIME retention. So probably this code should be rejected as mistaken? The same if the annotation has PARAMETER target only and the explicit non-compact canonical constructor is declared. With best regards, Tagir Valeev. From amaembo at gmail.com Sat Sep 19 09:29:07 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 19 Sep 2020 16:29:07 +0700 Subject: [records] Is C-style array declaration in components dropped? In-Reply-To: References: Message-ID: Hello! On Fri, Sep 18, 2020 at 8:47 PM Gavin Bierman wrote: > Actually it isn?t inconsistent as we need this text to allow for variable arity record component declarations. What is needed is to keep this as it is, but additionally to amend the three bullet points that follow to: > > - the element type that appears at the beginning of the declaration; then, > > - any bracket pairs that follow the variable's Identifier in the declarator > (not applicable for a variable arity parameter **or variable arity record > component**); then, > > - any bracket pairs that appear in the type at the beginning of the > declaration (where the ellipsis of a variable arity parameter **or variable > arity record component** is treated as a bracket pair). > > (Text marked **?** is new). Sounds good, thanks! With best regards, Tagir Valeev. > > Thanks, > Gavin From amaembo at gmail.com Sun Sep 20 01:49:10 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sun, 20 Sep 2020 08:49:10 +0700 Subject: Updated patterns-in-switch doc In-Reply-To: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> Message-ID: Hello! Found a couple of minutes to read this. Below are some of my thoughts. #1 Number of not covered primitive types. > with the possible temporary exception of the three primitive types not currently permitted I believe, there are four of them: boolean, long, double, and float Similarly, this part should be refined to mention long: > is limited to the integral numeric primitive types; this includes char but leaves out float, double and boolean. #2 Disallowing switch expressions inside guards > And worse if we allow switch expressions inside guards, which we shouldn't do. Hm... sounds like this may heavily complicate the grammar if we really want to prohibit switch expressions anywhere inside the guard. E.g.: switch (obj) { case Foo(int x) __where process(switch(x) {case 1 -> 10;case 2 -> 20;default -> 0;}) -> ... } Should this be allowed? If yes, then there's no point to disallow top-level switch expressions inside guards, as nested ones are confusing to the same level. If yes, then the grammar should be updated to include tons of productions like ExpressionNoSwitch, MethodInvocationNoSwitch, ArgumentListNoSwitch, and so on. To me, adding any restrictions to expressions inside guards looks an arbitrary decision. If users really want to write confusing code, let them allow using expression switches in guards. #3 Total patterns in instanceof > It is sensible because x instanceof is in some sense a silly question, in that it will always be true and there's a simpler way (local variable assignment) to express the same thing. It should be noted that local variable assignment requires the variable declaration which cannot be done inside the expression. However, the total pattern allows declaring temporary variables inside the expression. E.g. assuming class X { Foo doExpensiveCalculation(); } we can write today if (x != null && x.doExpensiveCalculation() != null && x.doExpensiveCalculation().isValidResult()) { use(x.doExpensiveCalculation()); } Using total instanceof we can declare new variable without explicit statement: if (x != null && x.doExpensiveCalculation() instanceof var foo && foo != null && foo.isValidResult()) { use(foo); } If this is prohibited, we will need to split `if` to two separate statements: if (x != null) { var foo = x.doExpensiveCalculation(); if (foo != null && foo.isValidResult()) { use(foo); } } Well, we may split declaration and assignment and put the assignment inside the condition: Foo foo; if (x != null && (foo = x.doExpensiveCalculation()) != null && foo.isValidResult()) { use(foo); } But this has at least two drawbacks: necessity to specify explicit Foo type (var doesn't work anymore) and broadening the scope of `foo` more than necessary (it pollutes the namespace after `if`). In general, it looks asymmetrical to me that the condition can introduce variables only if we can express the condition on that variable with a pattern, but we cannot do this if we have a guard-like condition. If the guard is considered as a part of the pattern (rather than a part of switch-case label grammar) then we could use the following syntax: if (x != null && x.doExpensiveCalculation() instanceof var foo __where foo != null && foo.isValidResult()) { use(foo); } In this case, the pattern is guarded, thus non-total, thus we can use it in instanceof. This also reads quite naturally to me, as __where explicitly says that the following condition refines the pattern, not just some unrelated, so it could be convenient to use guards on non-total patterns as well. To conclude, I think if we disallow total patterns in instanceof we should allow guards there. Otherwise, we are losing the convenience of introducing variables inside the expressions. #4 null-hostility of String/primitive box/enum switches only > For a switch on String, a primitive box type, or an enum type, if there is no explicit case null, we insert an implicit case null at the beginning of the switch that throws NPE. This looks a little bit suspicious to me, but probably we can preview and play with this rule. On one hand, looking only at switch selector expression, we cannot say without resolve whether it's an enum or a sealed type. And sometimes sealed types could be very similar to enums. Especially, taking into account that enhanced enums weren't implemented so we cannot have generic enum, but now we can emulate it with a generic sealed type whose subclasses are all singletons. So one may think of a specific type as of enum, but it's not actually an enum (thus the switch will unexpectedly tolerate null). On the other hand, if we disallow constant patterns, it looks like we can judge whether it's a enum/string/box-switch or pattern-switch checking looking at one of the case labels: labels for enum-switch and pattern-switch should look visually distinct (two identifier tokens for type pattern but only one token for enum/string/box constant). If we can be sure that patterns always differ visually from constant labels, then I think it's an ok idea. To conclude, given all our previous discussions about nullity and all the disadvantages of alternative designs, I think the newly proposed nullity design is good to be implemented as a preview feature. We can refine it in later preview rounds if it appears to be problematic. With best regards, Tagir Valeev On Tue, Sep 8, 2020 at 11:45 PM Brian Goetz wrote: > > I have updated > > https://github.com/openjdk/amber-docs/blob/master/site/design-notes/type-patterns-in-switch.md > > based on our discussions. From gavin.bierman at oracle.com Mon Sep 21 20:25:26 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 21 Sep 2020 21:25:26 +0100 Subject: [records] Specify use of @Override and @SafeVarargs on record components In-Reply-To: References: <023F93B1-0B21-4846-AFAE-AC97CB4C430A@oracle.com> Message-ID: <2BDFC9D9-905C-46EF-AB51-E38FDEBC1C8C@oracle.com> > On 19 Sep 2020, at 10:23, Tagir Valeev wrote: > > Hello! > > On Fri, Sep 18, 2020 at 10:40 PM Gavin Bierman wrote: > >> - It is annotated with the annotations, if any, that appear on the corresponding >> record component and whose annotation types are applicable in the method >> declaration context, or in type contexts, or both. **The rules for these >> annotation modifiers, if any, on the accessor method are the same as for a >> method declaration, and are specified in [9.7.4] and [9.7.5].** >> >> The last sentence is new, and mirrors what is said about normal method declarations in ?8.4.3. >> >> What do you think? > > I'm not very good in English but I'm fine if this statement > unambiguously imply that > if the annotation is not applicable to the method, then this is a > compilation error to put it on the record accessor. Well it?s as unambiguous as the existing text around annotations on method declarations :-) I think to further emphasise this point, I will add the following text after the list of properties of the implicitly declared accessor method: An implicitly declared accessor method must satisfy all the rules for a method in a normal class declaration ([8.4]). That makes it crystal clear (I hope). > > Btw, we allowed Override on explicit accessors, so now it might be > unclear whether it's allowed on implicit ones. > > Also what if we mark the record component with `@SafeVarargs` and > define an explicit accessor without this annotation? Like: > > record X(@SafeVarargs int y) { > @Override > public int y() { > return y; > } > } > > Is this acceptable? Javac 15 happily compiles this. Under the current rules this is acceptable; as the @SafeVarargs annotation is not propagated to the explicitly declared accessor method. > > This brings more general question: is it allowed to put an annotation > that targeted to METHOD only if we define explicit accessor? In this > case, the annotation is not propagated anywhere and completely > disappears from the bytecode, even if it has CLASS/RUNTIME retention. > So probably this code should be rejected as mistaken? That?s a very good question - well spotted. I think there is a good case to call that out as a compile-time error. Opinions everyone? Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 22 17:22:11 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 22 Sep 2020 13:22:11 -0400 Subject: Updated patterns-in-switch doc In-Reply-To: References: <0fdb5644-a67b-5e70-5cc0-de833f427129@oracle.com> Message-ID: <2609ccd7-3a9e-4847-af30-879ee40a2838@oracle.com> >> with the possible temporary exception of the three primitive types not currently permitted > I believe, there are four of them: boolean, long, double, and float RIght. > #2 Disallowing switch expressions inside guards > >> And worse if we allow switch expressions inside guards, which we shouldn't do. > Hm... sounds like this may heavily complicate the grammar if we really > want to prohibit switch expressions anywhere inside the guard. E.g.: > switch (obj) { > case Foo(int x) > __where process(switch(x) {case 1 -> 10;case 2 -> 20;default -> 0;}) -> ... > } > Should this be allowed? If yes, then there's no point to disallow > top-level switch expressions inside guards, as nested ones are > confusing to the same level. > If yes, then the grammar should be updated to include tons of > productions like ExpressionNoSwitch, MethodInvocationNoSwitch, > ArgumentListNoSwitch, and so on. > > To me, adding any restrictions to expressions inside guards looks an > arbitrary decision. If users really want to write confusing code, let > them allow using expression switches in guards. Let's separate out the grammar from the goal. Embedding switch expressions has two problems here; one is the obvious syntactic confusion (what switch is that case a part of?), and the other is side-effects; switch expressions are the only expressions that can embed statements.? Both are a bad match for expressing guard conditions.? (Note that when we did switch expressions, we omitted the possibility that a switch expression could be a constant expression, to avoid their use in, say, case labels in a constant switch: ??? case switch (x) { case 1 -> 3; case 2 -> 4; } -> 5; I think that was the right call :) As you pointed out yesterday on Twitter regarding old-style array declarations in record components, there are two ways to address it; constrain the grammar, or use a deliberately coarse grammar and then perform a post-parse check.? So if the answer is to constrain what you can put in guards, that doesn't mean we have to constrain the grammar. The concern about side-effects (which I know we can't fully contain) comes from a bigger goal, one that may not have been explicitly stated: I want that the computation inherent in a pattern switch effectively be "constant", for a number of reasons.? Having side-effects in case labels or guards effectively undermines that. One benefit (but not the only) of doing this is that it is a necessary condition for encoding the entire switch logic in an `indy` bootstrap, so that we can dynamically construct a decision tree based on the characteristics of the case labels.? The more imperative a switch looks, the harder it is to do that.? (So, for example, guards should probably be restricted to capturing effectively-final locals.) THe remaining constraining of side-effects will come from making only vague promises about when, and how often, to execute pattern bodies.? if we have a switch with: ??? case Foo(var x) where x == 0: ??? case Foo(var x) where x > 0: ??? case Foo(var x): we should be free to execute the deconstructor body early or just-in-time, once or three times (or more!) > #3 Total patterns in instanceof > >> It is sensible because x instanceof is in some sense a silly question, in that it will always be true and there's a simpler way (local variable assignment) to express the same thing. > It should be noted that local variable assignment requires the > variable declaration which cannot be done inside the expression. Yes, I realize that one can use pattern matching as a form of implicit assignment.? But if we think that is important, maybe it is better to try to get there more directly, rather than relying on a "trick".?? The idiom you describe -- pulling a DU local into a broader scope -- works, but is less than ideal, because you don't get the perfect scoping. > Well, we may split declaration and assignment and put the assignment > inside the condition: > > Foo foo; > if (x != null && (foo = x.doExpensiveCalculation()) != null && > foo.isValidResult()) { > use(foo); > } > > But this has at least two drawbacks: necessity to specify explicit Foo > type (var doesn't work anymore) and broadening the scope of `foo` more > than necessary (it pollutes the namespace after `if`). In general, it > looks asymmetrical to me that the condition can introduce variables > only if we can express the condition on that variable with a pattern, > but we cannot do this if we have a guard-like condition. Here's how I would rather write that (if the status quo isn't good enough): ??? if (x != null && (var foo = x.doExpensiveCalculation()) != null && foo.isValidResult()) { use(foo); } That is, treat `var x = e` as a pattern match that always succeeds and generates one binding. From john.r.rose at oracle.com Wed Sep 23 19:14:03 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 23 Sep 2020 12:14:03 -0700 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: References: Message-ID: <33817D67-EEDF-4E0A-A68E-AB032B075AA3@oracle.com> On Aug 26, 2020, at 8:00 AM, Brian Goetz wrote: > > ?I now think that we have done the same with binding variables. Good catch; thank you! > Here are two motivating examples: > > (a) Pattern assignment? > (b) Reconstruction. We have analogized that a `with` expression: > > x with { B } > > is like the block expression: > > { X(VARS) = x; B /* mutates vars */; yield new X(VARS) } > > except that mutating the variables would not be allowed. I am convinced that (under broad presuppositions) reconstruction expressions *should* allow mutation to their internal bindings. The reason for this is to allow locally imperative code to update the immutable state of an object that contains the state, so as to produce an object that contains the updated state. This is a key use case for refactoring loops using a[i++] and i.next() into idioms that use inline objects, specifically cursors, for managing iteration. for (var c = Arrays.cursor(a, start, end); c.hasNext(); c = c __With { index++; }) ? for (var c = myList.cursor(); c.hasNext(); c = c.afterNext()) ? Internally, c.afterNext() uses a self-reconstructor with some sort of index++ just like the old iterator does: public MyListCursor afterNext() { return this __With { i++; }; } Please see for more details: http://cr.openjdk.java.net/~jrose/values/iterator-vs-cursor.html ? John P.S. Once you buy into this set of use cases, numerous sugary improvements to ?__With? become apparent by analogy with things like lambda bodies and object constructors and compound assignment operators. I?ll leave that as an exercise for the EG. P.P.S. Both cursors and iterators could support a very nifty new loop idiom using ?non-static? patterns: for (var i = myContainer.iterator(); i.nextIs(var e); ) { ? do something with e ? } where Iterator has a new default method like this: interface Iterator { ? default __Pattern nextIs() __Outputs(T element) { if (!hasNext()) __Fail; // aka `break;` or `return false;` element = next(); // implicit `return;` aka __Match aka __Match(element) } From john.r.rose at oracle.com Thu Sep 24 00:57:11 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 23 Sep 2020 17:57:11 -0700 Subject: Finalizing in JDK 16 - Pattern matching for instanceof In-Reply-To: <33817D67-EEDF-4E0A-A68E-AB032B075AA3@oracle.com> References: <33817D67-EEDF-4E0A-A68E-AB032B075AA3@oracle.com> Message-ID: <543FF675-5F18-437B-87CF-B580F2C6168F@oracle.com> On Sep 23, 2020, at 12:14 PM, John Rose wrote: > I am convinced that (under broad presuppositions) reconstruction > expressions *should* allow mutation to their internal bindings. P.P.P.S. I updated my case study [1] to point at Brian?s more recent document [2], and realized that my ?broad presuppositions? are exceedingly broad, and probably nobody is arguing *against* something like with-blocks. Still, I would like to go on record as preferring fully imperative blocks, with the option to specify localized side effects like `i++` and/or control flow, to the alternative syntaxes on the table. Those would be, AFAIK: (a) positionally-parameterized constructors and factories *only* (as in `p=Point.of(p.x,p.y+1)`), and (b) a halfway point of constructors and factories which allow named arguments with suitably tuned defaults (as in `p=p.with(y: p.y+1)`). (Brian has a clever way to desugar general with-blocks into call-with-named-arguments, under the heading ?Making names significant?. And that would be super-useful in its own right. But I still like the fully imperative with-block far more than (b).) [1]: http://cr.openjdk.java.net/~jrose/values/iterator-vs-cursor.html [2]: https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md From gavin.bierman at oracle.com Mon Sep 28 10:21:09 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 28 Sep 2020 11:21:09 +0100 Subject: [records] Spec for next version of Record Classes JEP Message-ID: Dear all: A draft of the specs for the Record Classes feature that we plan to finalize in JDK 16 is now available: http://cr.openjdk.java.net/~gbierman/8246771/latest/ [NB: The URL will change once we have a JEP number, and will be announced.] The changes are the same as those in the second preview that was released in Java SE 15, except for minor editorial changes and the following: - To relax the current restriction on an inner class from declaring a member that is explicitly or implicitly static. This will now be permitted and, in particular, will allow an inner class to declare a record class member. (These changes are detailed in the companion document "Local and Nested Static Declarations"). - Add text to explicitly rule out using C-style array declaration of record components. - Clarify that any annotations on record components that apply to the implicitly declared accessor method must satisfy the existing rules for annotating a method declaration. - A new section (8.10.5) defining new restrictions on annotations of record components to ensure that they are not lost. This last point addresses the case highlighted by Tagir (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-September/002560.html). There?s one minor corner-case that I bring to your attention. Consider the following: @Target(ElementType.METHOD) @interface A { } record R(@A int x) { int x() { return this.x; } } The new rules ensure that this is an error as the annotation on the record component is not propagated anywhere because of the explicit accessor declaration. However, what if the accessor was annotated with the same annotation? @Target(ElementType.METHOD) @interface A { } record R(@A int x) { @A int x() { return this.x; } } As it stands, the spec rules this out as an error. For simple annotations, equality is simple to define, but do we want to attempt to define it for all kinds of annotations? This feels like it?s not worth the complexity, but I?d be happy to hear opinions. Many thanks, Gavin From amaembo at gmail.com Tue Sep 29 15:50:24 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 29 Sep 2020 22:50:24 +0700 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: Hello! Thank you for the spec update! > However, what if the accessor was annotated with the same annotation? I would play simple: if the accessor exists, the METHOD-targeted annotation is illegal, even if accessor is annotated in the same way. In other words, to me, section 8.10.5 looks ok and doesn't require changes. The only nit-pick is that it says "if a canonical constructor is declared explicitly". However, from 8.10.4 one may derive that compact constructor is a canonical constructor, so if compact canonical constructor is explicitly declared then, per 8.10.5 it looks like annotation should be discarded but it's not. Probably it's better to write in 8.10.5 something like "if a non-compact canonical constructor is declared explicitly". Well, probably I'm seeing things. With best regards, Tagir Valeev. On Mon, Sep 28, 2020 at 5:21 PM Gavin Bierman wrote: > > Dear all: > > A draft of the specs for the Record Classes feature that we plan to finalize in > JDK 16 is now available: > > http://cr.openjdk.java.net/~gbierman/8246771/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the second preview that was released in > Java SE 15, except for minor editorial changes and the following: > > - To relax the current restriction on an inner class from declaring a member > that is explicitly or implicitly static. This will now be permitted and, in > particular, will allow an inner class to declare a record class member. (These > changes are detailed in the companion document "Local and Nested Static Declarations"). > > - Add text to explicitly rule out using C-style array declaration of record > components. > > - Clarify that any annotations on record components that apply to the implicitly > declared accessor method must satisfy the existing rules for annotating a > method declaration. > > - A new section (8.10.5) defining new restrictions on annotations of record > components to ensure that they are not lost. > > > This last point addresses the case highlighted by Tagir > (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-September/002560.html). > > > There?s one minor corner-case that I bring to your attention. Consider the > following: > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > int x() { return this.x; } > } > > The new rules ensure that this is an error as the annotation on the record > component is not propagated anywhere because of the explicit accessor > declaration. However, what if the accessor was annotated with the same > annotation? > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > @A int x() { return this.x; } > } > > As it stands, the spec rules this out as an error. For simple annotations, > equality is simple to define, but do we want to attempt to define it for all > kinds of annotations? This feels like it?s not worth the complexity, but I?d be > happy to hear opinions. > > Many thanks, > Gavin From brian.goetz at oracle.com Tue Sep 29 21:32:55 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Sep 2020 17:32:55 -0400 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: <2238a301-1a62-bf23-73d7-be1f29f9ee84@oracle.com> In 6.1, you allude to implicitly declared fields of a record, but not implicitly declared accessor methods or constructors.? Is this an omission? In 8.1.1, I see local records and enums (and they are static), but I don't see local interfaces.? I assume we are saving those for a future pass (along with static members in inner classes?) Should @SafeVarargs be allowed to be applied to a varargs record?? Should it get propagated to the constructor in that case?? Or do we just have people declare a compact constructor with @SafeVarargs?? What is the correct use of @SV for varargs records? On 9/28/2020 6:21 AM, Gavin Bierman wrote: > Dear all: > > A draft of the specs for the Record Classes feature that we plan to finalize in > JDK 16 is now available: > > http://cr.openjdk.java.net/~gbierman/8246771/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the second preview that was released in > Java SE 15, except for minor editorial changes and the following: > > - To relax the current restriction on an inner class from declaring a member > that is explicitly or implicitly static. This will now be permitted and, in > particular, will allow an inner class to declare a record class member. (These > changes are detailed in the companion document "Local and Nested Static Declarations"). > > - Add text to explicitly rule out using C-style array declaration of record > components. > > - Clarify that any annotations on record components that apply to the implicitly > declared accessor method must satisfy the existing rules for annotating a > method declaration. > > - A new section (8.10.5) defining new restrictions on annotations of record > components to ensure that they are not lost. > > > This last point addresses the case highlighted by Tagir > (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-September/002560.html). > > > There?s one minor corner-case that I bring to your attention. Consider the > following: > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > int x() { return this.x; } > } > > The new rules ensure that this is an error as the annotation on the record > component is not propagated anywhere because of the explicit accessor > declaration. However, what if the accessor was annotated with the same > annotation? > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > @A int x() { return this.x; } > } > > As it stands, the spec rules this out as an error. For simple annotations, > equality is simple to define, but do we want to attempt to define it for all > kinds of annotations? This feels like it?s not worth the complexity, but I?d be > happy to hear opinions. > > Many thanks, > Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 30 14:54:53 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Sep 2020 10:54:53 -0400 Subject: What does "total" really mean? In-Reply-To: <4dd27f07-391a-2ff8-072b-51897fc31f6b@oracle.com> References: <4dd27f07-391a-2ff8-072b-51897fc31f6b@oracle.com> Message-ID: > Secondarily, perhaps totality isn't the right term; maybe we need a > word for "good enough to satisfy the checker", where the checker is > generous in letting us be sloppy regarding silly values. In searching for names for "total enough to satisfy the type checker", it briefly occurred to me to make an appeal to the notion from real analysis of "almost everwhere" (often written a.e., or Remi might have seen this as p.p.): ??? https://en.wikipedia.org/wiki/Almost_everywhere Though, the analogy isn't quite right because a.e. is not picky about what measure-zero set the property does not hold.? Here, we want to outline a _specific_ set on which the property is allowed to not hold. We toyed with "optimistically total", which is appealing because the cases that are covered are the ones we hope will never show up (hence the optimism.) Another variant of totality is "effectively total".? We've used the phrase "effectively final" to mean "you didn't say final, but I figured it out anyway."? The same could apply to things like covering all the subtypes of a sealed type.? It is a more friendly name than o.t., but it may not be as obvious that there's a strange-shaped remainder. There might also be phrases that don't include the t-word, but I think a modifier on the t-word is probably better.? What I like about a modifier is that true totality is a degenerate case of {almost, effectively, optimistically}-total. -------------- next part -------------- An HTML attachment was scrubbed... URL: