From forax at univ-mlv.fr Thu Jan 2 20:52:34 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 2 Jan 2020 21:52:34 +0100 (CET) Subject: Enum vs Record vs Sealed types Message-ID: <359481003.236548.1577998354903.JavaMail.zimbra@u-pem.fr> Now that the design of Record is mostly stabilized, i think we should talk about the relation between an enum, a record and a sealed type. As we have done with records, we can work backward from the pattern matching switch to see what we need for an enum. So let suppose that like Java2D, we fill a shape with a Paint, Paint being an interface between a solid color and a gradient. sealed Paint permits Color, LinearGradient { } enum Color implements Paint{ RED(255, 0, 0), BLACK(0, 0, 0), WHITE(255, 255, 255); private final int red, green, blue; ... } record LinearGradient(Point start, Color startColor, Point end, Color endColor) implements Paint { int getRed(int x, int y) { ... } int getGreen(int x, int y) { ... } int getBlue(int x, int y) { ... } } In that case, we may want to be able to switch on a special value (like RED), on an enum and on a Gradient which is a record. int redAt(Paint paint, int x, int y) { return switch(paint) { case RED -> 255; case Color(var red, _, _) -> red; case LinearGradient gradient -> gradient.getRed(x, y); }; } So should we allow record components on an enum with the following rule: - either an enum use the record like syntax enum Color(int red, int green, int blue) { ... } and in that case, no supplementary fields are allowed. - or an enum doesn't use the record like syntax and in that case, it's the classical enum. The other solution, is to wait to have de-constructor, in that case we may not need a special syntax for enum. R?mi From forax at univ-mlv.fr Thu Jan 2 21:08:47 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 2 Jan 2020 22:08:47 +0100 (CET) Subject: Deconstructor return type as record ? Message-ID: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> We have introduce records because it's a simple abstraction to extract values when doing the pattern matching. Given that records are named tuples, does it means that a deconstructor is just a classical method with no parameter and a record as return type ? public class Point { private final double rho, theta; public Point(double x, double y) { rho = Math.hypot(x, y); teta = Math.atan(y / x); } public record RectangularPoint(double x, double y) { } public RectangularPoint deconstructor() { return new RectangularPoint(rho * Math.cos(theta), rho * sin(theta)); } } and with a little syntactic sugar to see any tuples as a Record (like we see any lambdas as a functional interface) public RectangularPoint deconstructor() { return (rho * Math.cos(theta), rho * sin(theta)); } R?mi From brian.goetz at oracle.com Thu Jan 2 21:22:44 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 2 Jan 2020 16:22:44 -0500 Subject: Deconstructor return type as record ? In-Reply-To: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> References: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> Message-ID: <386413bf-2e1c-c9eb-73a9-20f2292f07d7@oracle.com> There are at least two questions here, one having to do with records, the other having to do with pattern matching. Recall, in Lambda, how we chose to do nominal function types (functional interfaces) rather than structural ones.? Records make a similar choice -- rather than doing structural _tuples_, we choose to do nominal ones.? So the question you are raising becomes: is there an equivalent of "SAM conversion" for records, as there was for lambdas?? In your example, you posit one example: that an expression of the syntactic form (x,y) can be converted to a record of the right shape. I think the answer here is "we're still not sure".? It's been an idea that's been sitting around in the idea pile for a while, but so far I've not been thrilled with some of the ways we might have exposed it.? Ideas like this are superficially attractive, but they don't go very deep; they only optimize the instantiation.? And, while you could say the same for lambdas vs inner classes, in the tuple-vs-constructor example, the difference is much smaller than it was for lambdas vs inner classes (both syntactically and semantically.)? So I think this is indeed sugar, and the sugar goes on last. As to the pattern matching aspect, I know you've been wanting to have the "what does a deconstructor look like" discussion for a long time, but we're still not ready to have that discussion.? I promise, these requests have not been forgotten, and we'll bring it up when we're ready to! On 1/2/2020 4:08 PM, Remi Forax wrote: > We have introduce records because it's a simple abstraction to extract values when doing the pattern matching. > Given that records are named tuples, does it means that a deconstructor is just a classical method with no parameter and a record as return type ? > > public class Point { > private final double rho, theta; > > public Point(double x, double y) { > rho = Math.hypot(x, y); > teta = Math.atan(y / x); > } > > public record RectangularPoint(double x, double y) { } > public RectangularPoint deconstructor() { > return new RectangularPoint(rho * Math.cos(theta), rho * sin(theta)); > } > } > > and with a little syntactic sugar to see any tuples as a Record (like we see any lambdas as a functional interface) > public RectangularPoint deconstructor() { > return (rho * Math.cos(theta), rho * sin(theta)); > } > > > R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Jan 2 22:15:44 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 2 Jan 2020 23:15:44 +0100 (CET) Subject: Deconstructor return type as record ? In-Reply-To: <386413bf-2e1c-c9eb-73a9-20f2292f07d7@oracle.com> References: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> <386413bf-2e1c-c9eb-73a9-20f2292f07d7@oracle.com> Message-ID: <648394439.239657.1578003344825.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" , "amber-spec-experts" > > Envoy?: Jeudi 2 Janvier 2020 22:22:44 > Objet: Re: Deconstructor return type as record ? > There are at least two questions here, one having to do with records, the other > having to do with pattern matching. > Recall, in Lambda, how we chose to do nominal function types (functional > interfaces) rather than structural ones. Records make a similar choice -- > rather than doing structural _tuples_, we choose to do nominal ones. So the > question you are raising becomes: is there an equivalent of "SAM conversion" > for records, as there was for lambdas? In your example, you posit one example: > that an expression of the syntactic form (x,y) can be converted to a record of > the right shape. > I think the answer here is "we're still not sure". It's been an idea that's been > sitting around in the idea pile for a while, but so far I've not been thrilled > with some of the ways we might have exposed it. Ideas like this are > superficially attractive, but they don't go very deep; they only optimize the > instantiation. And, while you could say the same for lambdas vs inner classes, > in the tuple-vs-constructor example, the difference is much smaller than it was > for lambdas vs inner classes (both syntactically and semantically.) So I think > this is indeed sugar, and the sugar goes on last. It depends if you teach the compiler/VM to consider the record created inside the deconstructor to be an inline even if it is not declared as such. In that case, a deconstructor is close to a zero cost abstraction. > As to the pattern matching aspect, I know you've been wanting to have the "what > does a deconstructor look like" discussion for a long time, but we're still not > ready to have that discussion. I promise, these requests have not been > forgotten, and we'll bring it up when we're ready to! yes, i would like to move on that subject to have a better idea of the input/output of the pattern matching in term of data. And i think that instead of try to create a design that encompass every cases before trying to play with it, i think we should try to do simple pattern matching with extraction and see how we can grow from that. R?mi > On 1/2/2020 4:08 PM, Remi Forax wrote: >> We have introduce records because it's a simple abstraction to extract values >> when doing the pattern matching. >> Given that records are named tuples, does it means that a deconstructor is just >> a classical method with no parameter and a record as return type ? >> public class Point { >> private final double rho, theta; >> public Point(double x, double y) { >> rho = Math.hypot(x, y); >> teta = Math.atan(y / x); >> } >> public record RectangularPoint(double x, double y) { } >> public RectangularPoint deconstructor() { >> return new RectangularPoint(rho * Math.cos(theta), rho * sin(theta)); >> } >> } >> and with a little syntactic sugar to see any tuples as a Record (like we see any >> lambdas as a functional interface) >> public RectangularPoint deconstructor() { >> return (rho * Math.cos(theta), rho * sin(theta)); >> } >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Thu Jan 2 22:25:21 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 2 Jan 2020 14:25:21 -0800 Subject: Enum vs Record vs Sealed types In-Reply-To: <359481003.236548.1577998354903.JavaMail.zimbra@u-pem.fr> References: <359481003.236548.1577998354903.JavaMail.zimbra@u-pem.fr> Message-ID: On Jan 2, 2020, at 12:52 PM, Remi Forax wrote: > > The other solution, is to wait to have de-constructor, in that case we may not need a special syntax for enum. Yeah, that?s my first thought on this. Given a deconstructor which we certainly will have, the incremental advantage to record-like enums is very limited. And there are disadvantages: Some of the record and enum contracts are contradictory. Happy new year! ? John From john.r.rose at oracle.com Fri Jan 3 10:11:17 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 3 Jan 2020 02:11:17 -0800 Subject: Deconstructor return type as record ? In-Reply-To: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> References: <515710754.237191.1577999327197.JavaMail.zimbra@u-pem.fr> Message-ID: <259D4BB9-F482-4E8D-9603-1012E241E18F@oracle.com> On Jan 2, 2020, at 1:08 PM, Remi Forax wrote: > > Given that records are named tuples, does it means that a deconstructor is just a classical method with no parameter and a record as return type ? Brian has given a good answer to this from the POV of language development, and I agree with it. From the POV of the JVM, looking upward through the translation strategy, I think it is best *not* to expose or commit to unnecessary details about the type of the return value of a deconstructor. It would be reasonable to provide combinators which *convert* the result of a deconstruction into some ad hoc desired nominal form, but it is not reasonable to mandate such a nominal form for everyone to stick to, because it will make it harder to evolve (by adding constraints to) the runtime operations necessary to link back to result of a deconstruction. So, from a JVM POV, a deconstructor should return an existential type (embodied in an Object pointer) whose concrete representation is chosen by an invokedynamic BSM, based on the exact requirements of the call site. There are lots of ways to fill out such a story. Crucially, it is possible to make nontrivial changes to a story on the fly, without recompiling the indy sites. Also, I don?t think it prejudices the choices we may wish to make in the language and user model. So: Yes, allow the result of a match to be exposed as a requested record type (with an optional wrapped around it if non-total), but No, don?t mandate this in the type signatures of the involved methods, not in the bytecode, the reflection, or the source language. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 3 21:48:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 3 Jan 2020 16:48:28 -0500 Subject: Enum vs Record vs Sealed types In-Reply-To: <359481003.236548.1577998354903.JavaMail.zimbra@u-pem.fr> References: <359481003.236548.1577998354903.JavaMail.zimbra@u-pem.fr> Message-ID: I agree that all of these pattern matches should be expressible. Under the current plan, we get there (at least) by: ?- RED is a constant pattern (well, Color.RED is a constant pattern, anyway.) ?- Color(var red, _, _) is a deconstruction pattern, with an explicit deconstructor The feature you are suggesting -- call it "record-style enum declarations", is a sound one; such an enum would acquire fields, constructors, deconstructors, and accessors, just like records.? It has come up a few times already; the reason we haven't done it so far is that the return-on-complexity is less than for records.? (It has its share of extra spec and compiler behavior, but the user benefit is less -- until we get to deconstruction patterns, it is just being able to omit the field and ctor declarations, which is something but not all that much.)? So we are holding this feature "on the shelf" until it seems more likely to pay its way. (Further, we will want the exhaustiveness analyzer to be able to determine that the following switch is exhaustive: ??? switch (paint) { ??????? case RED: ... ??????? case BLACK: ... ??????? case WHITE: ... ??????? case LinearGradient(...): ... ??? } by observing that RED/BLACK/WHITE covers Color, and Color+LinearGradient covers Paint.) On 1/2/2020 3:52 PM, Remi Forax wrote: > Now that the design of Record is mostly stabilized, i think we should talk about the relation between an enum, a record and a sealed type. > > As we have done with records, we can work backward from the pattern matching switch to see what we need for an enum. > > So let suppose that like Java2D, we fill a shape with a Paint, Paint being an interface between a solid color and a gradient. > sealed Paint permits Color, LinearGradient { } > enum Color implements Paint{ > RED(255, 0, 0), BLACK(0, 0, 0), WHITE(255, 255, 255); > private final int red, green, blue; > ... > } > record LinearGradient(Point start, Color startColor, Point end, Color endColor) implements Paint { > int getRed(int x, int y) { ... } > int getGreen(int x, int y) { ... } > int getBlue(int x, int y) { ... } > } > > In that case, we may want to be able to switch on a special value (like RED), on an enum and on a Gradient which is a record. > > int redAt(Paint paint, int x, int y) { > return switch(paint) { > case RED -> 255; > case Color(var red, _, _) -> red; > case LinearGradient gradient -> gradient.getRed(x, y); > }; > } > > So should we allow record components on an enum with the following rule: > - either an enum use the record like syntax > enum Color(int red, int green, int blue) { ... } > and in that case, no supplementary fields are allowed. > - or an enum doesn't use the record like syntax and in that case, it's the classical enum. > > The other solution, is to wait to have de-constructor, in that case we may not need a special syntax for enum. > > R?mi From amaembo at gmail.com Sat Jan 4 15:35:14 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 4 Jan 2020 22:35:14 +0700 Subject: [records] Time to re-read JEP 359 Message-ID: Hello! I was watching a Twitch stream by Nicolai Parlog [1] who explored the records feature. Quite expectedly he did this reading JEP 359, rather than the spec draft. He noticed at least two inconsistencies: 1. The record's body may declare static methods, static fields, static initializers, constructors, instance methods, instance initializers, and nested types. "intance initializers" part should be removed, to match the spec draft. 2. Any of the members that are automatically derived from the state description can also be declared explicitly. private final fields that match the record components are members and derived from the state description, so from this statement, one could conclude that explicit field declaration is also possible (which is not an unreasonable thing to do: one may want to customize the field annotations). If we don't allow such a declaration, this statement should be refined (probably changing 'members' to 'methods and constructor' or 'members except field' or something like this). With best regards, Tagir Valeev [1] https://www.twitch.tv/videos/529899179 (link may become invalid in a month or so) From brian.goetz at oracle.com Mon Jan 6 15:42:51 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 6 Jan 2020 10:42:51 -0500 Subject: [records] Time to re-read JEP 359 In-Reply-To: References: Message-ID: On 1/4/2020 10:35 AM, Tagir Valeev wrote: > Hello! > > I was watching a Twitch stream by Nicolai Parlog [1] who explored the > records feature. Quite expectedly he did this reading JEP 359, rather > than the spec draft. He noticed at least two inconsistencies: > > 1. The record's body may declare static methods, static fields, static > initializers, constructors, instance methods, instance initializers, > and nested types. > > "intance initializers" part should be removed, to match the spec draft. Done. > 2. Any of the members that are automatically derived from the state > description can also be declared explicitly. > > private final fields that match the record components are members and > derived from the state description, so from this statement, one could > conclude that explicit field declaration is also possible (which is > not an unreasonable thing to do: one may want to customize the field > annotations). If we don't allow such a declaration, this statement > should be refined (probably changing 'members' to 'methods and > constructor' or 'members except field' or something like this). > This is a fair question; do we want to allow this?? There are several reasons one might want to do so: ?- Consistency (though this is generally the reason you give when you have no other reason), you can do it for the other members; ?- Maybe the author wants the fields public; ?- Maybe the author wants annnotations on the fields that, were they to be put on the components, would be spread to places where they are _not_ wanted. The last of these is the only one that is mildly compelling here, but, I wonder whether this is purely theoretical, or whether someone actually needs this. The reasons against include: ?- It is not likely to be something people want to do very often, and so the return on spec complexity here is probably negative. From amaembo at gmail.com Mon Jan 6 16:10:56 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 6 Jan 2020 23:10:56 +0700 Subject: [records] Time to re-read JEP 359 In-Reply-To: References: Message-ID: Hello! ??, 6 ???. 2020 ?., 22:42 Brian Goetz : > - Maybe the author wants annnotations on the fields that, were they to > be put on the components, would be spread to places where they are _not_ > wanted. > > The last of these is the only one that is mildly compelling here, but, I > wonder whether this is purely theoretical, or whether someone actually > needs this. > I'm for not allowing this. I cannot imagine that custom annotation could be necessary. I saw many kinds of annotations but I don't think there's some scenario when it's necessary to annotate a private field of record and it cannot be covered by the corresponding annotation of record component or explicit accessor. Many annotations simply don't applicable to record fields. E.g. dependency injection like @Inject or @Autowired: this should be done via canonical constructor instead of reflection. Or Lombok's @Getter (who needs Lombok getters in record?), @Setter (field is final, no setter is possible). JPA injections are also should be done via the constructor, so record component annotation should be better. Static analyzer suppression annotations are also unlikely necessary: what could be suppressed for implicitly defined fields? Well, it could be desired for nullability, e.g.: record ListWrapper(@Nullable List list) { public ListWrapper { this.list = list == null ? List.of() : list; } public @NotNull List list() { return list; } // IDE might warn here that you are returning nullable field from a method marked as not-null. } In this particular case, it could be desired to specify an explicit @NotNull on the field. However, this could be solved defaulting to @NotNull instead: record ListWrapper(@NotNull List list) { public ListWrapper(@Nullable List list) { // need to specify args here, but no need to override accessor method this.list = list == null ? List.of() : list; } } Even though we cannot use a compact constructor anymore, this looks clean solution to me. In any case, we can allow it later if somebody comes up with an example where it's really necessary. With best regards, Tagir Valeev. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jan 6 16:15:41 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 6 Jan 2020 11:15:41 -0500 Subject: [records] Time to re-read JEP 359 In-Reply-To: References: Message-ID: <4f1cc0d7-280a-d5a1-650c-27e453e9959b@oracle.com> I mostly agree.? For the "record", here's one more possible reason I left out: ?- So you can put Javadoc on the fields. Again, I don't find this one particularly compelling, but I wanted to put it in the record. On 1/6/2020 11:10 AM, Tagir Valeev wrote: > Hello! > > ??, 6 ???. 2020 ?., 22:42 Brian Goetz >: > > ??- Maybe the author wants annnotations on the fields that, were > they to > be put on the components, would be spread to places where they are > _not_ > wanted. > > The last of these is the only one that is mildly compelling here, > but, I > wonder whether this is purely theoretical, or whether someone > actually > needs this. > > > I'm for not allowing this. I cannot imagine that custom annotation > could be necessary. I saw many kinds of annotations but I don't think > there's some scenario when it's necessary to annotate a private field > of record and it cannot be covered by the corresponding annotation of > record component or explicit accessor. Many annotations simply don't > applicable to record fields. E.g. dependency injection like @Inject or > @Autowired: this should be done via canonical constructor instead of > reflection. Or Lombok's @Getter (who needs Lombok getters in record?), > @Setter (field is final, no setter is possible). JPA injections are > also should be done via the constructor, so record component > annotation should be better. Static analyzer suppression annotations > are also unlikely necessary: what could be suppressed for implicitly > defined fields? > > Well, it could be desired for nullability, e.g.: > > record ListWrapper(@Nullable List list) { > ? public ListWrapper { > ? ? this.list = list == null ? List.of() : list; > ? } > > ? public @NotNull List list() { return list; } // IDE might warn > here that you are returning nullable field from a method marked as > not-null. > } > > In this particular case, it could be desired to specify an > explicit?@NotNull on the field. However, this could be solved > defaulting to @NotNull instead: > > record ListWrapper(@NotNull List list) { > ? public ListWrapper(@Nullable List list) { // need to specify args > here, but no need to override accessor method > ? ? this.list = list == null ? List.of() : list; > ? } > } > > Even though we cannot use a compact constructor anymore, this looks > clean solution to me. > > In any case, we can allow it later if somebody comes up with an > example where it's really necessary. > > With best regards, > Tagir Valeev. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jan 7 20:30:27 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 7 Jan 2020 15:30:27 -0500 Subject: Towards cleaner nesting Message-ID: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> Everything about nesting in Java is a mess.? The terminology is a mess (top level classes, nested classes, inner classes, local classes, anonymous classes); the set of restrictions on what can nest in what is ad-hoc (can have local classes but not local interfaces; inner classes cannot have static members, including static nested classes), and the set of rules about what must be, can be, or cannot be static is also ad-hoc (nested classes can be static or not, nested interfaces are implicitly static, but local and anonymous classes may not be static, even though it might make sense.)? On top of that, we can nest classes in methods (sometimes) and methods in classes but not methods in methods (local methods). Not only does this make for a lot of accidental complexity in specification, implementation, and user's brains, but it means every feature interact with this complexity.? Nested records are implicitly static, but this meant that in 14 we can't have nested records in non-static classes, because, non-static classes can't have static members.? (Yes, this could be fixed; hold your "why don't you just" suggestions.)? And we borked up the implementation of local records the first time around, where they accidentally capture effectively final locals, which they shouldn't -- because we'd never really charted the "static local class" territory, and got it wrong the first time.? (Yes, this can be fixed too, and will be before 14 goes out.) So, I'd like to propose a simpler, general story of nesting (which is consistent with the ad-hoc rubbish we have) which we can get to in stages.? The purpose of this mail is to discuss the model; in what increments we get there is a separate story. Goals: ?- Anything (class, interface, record, enum, method) can be nested in anything; ?- Some things are always static (enums, records, interfaces) when nested; the rest can be made static when desired; ?- The rule about "no static members in nonstatic nested classes" has to go; ?- Rules about whether members / locals from enclosing contexts can be specified in a single place, using local reasoning. The core of this is coming to an understanding of what "static" means.? When construct X nests in Y (whether X and Y are classes, methods, interfaces, etc), for "X" to be "static" means that nesting is being used purely for purposes of namespacing, and not for purposes of having access to names (locals or nonstatic class members) from enclosing constructs. Unfortunately all the terms we might use for whether or not a symbol in an outer construct can be used in a nested construct -- such as "accessible" -- are overloaded with other meanings. For purposes of this discussion, let's call this "capturable" (this is also overloaded, but less so.)? Each construct (class type or method) has two sets of names from outer constructs that are capturable -- a _statically capturable_ set SC(X), and a _non-statically capturable_ set NC(X).? We can define capturability using local reasoning: Base cases: ?- Names of static members in X are in SC(X); ?- Names of instance members of X (if X is a class) or effectively final locals of X (if X is a method) are in NC(X); Induction cases, where X is nested directly in Y: ?- SC(Y) is in SC(X) ?- If _X is not static_, then NC(Y) is in NC(X) We then say that X can capture names in SC(X) and NC(X); all we need to compute capturability is the capture sets of X's immediately enclosing construct, and whether X is static or not in that construct (modulo shadowing etc.) For the math-challenged, what this means is: ?- A nested construct can access static members of all the enclosing constructs; ?- A nested non-static construct can access instance members and effectively final locals of all enclosing constructs, up until we hit a static construct, and then capturing stops.? (So if Z is nested in Y is nested in static X, Z can access instance members / eff final locals of Y and X but not anything non-static from outside of X.) Note that this is consistent with what currently happens when X is a method as well as a class type; static methods in a class "capture" the static members of the enclosing class, and instance methods also capture the instance members of the enclosing class -- and also consistent with capturing in lambdas and anonymous classes, if we assume that these are always non-static constructs. We then say enums, records, and interfaces are _always_ static when nested, whether declared so or not, we eliminate the restriction about static members in non-static nested classes (now that we have a clear semantics for them), and allow local classes to be declared as static.? (Eventually, we also relax the restrictions about methods in methods, static or not.) (Additionally, the model supports the notion of "static lambda" and "static anonymous class" with obvious semantics (can't capture anything); we can decide later whether adding this flexibility is worth the additional surface syntax.) This is a strict superset of the status quo, and yields a more flexible and regular language -- and hopefully a simpler spec (since so many of these cases are specified as ad-hoc corner cases.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Jan 7 21:27:43 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 7 Jan 2020 22:27:43 +0100 (CET) Subject: Towards cleaner nesting In-Reply-To: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> Message-ID: <2014421213.1189937.1578432463507.JavaMail.zimbra@u-pem.fr> Hi Brian, Nice sump-up, i like it very much. I believe we also need to think about type parameters, they are also impacted by the nesting/static context so should be in NC(X). R?mi > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Mardi 7 Janvier 2020 21:30:27 > Objet: Towards cleaner nesting > Everything about nesting in Java is a mess. The terminology is a mess (top level > classes, nested classes, inner classes, local classes, anonymous classes); the > set of restrictions on what can nest in what is ad-hoc (can have local classes > but not local interfaces; inner classes cannot have static members, including > static nested classes), and the set of rules about what must be, can be, or > cannot be static is also ad-hoc (nested classes can be static or not, nested > interfaces are implicitly static, but local and anonymous classes may not be > static, even though it might make sense.) On top of that, we can nest classes > in methods (sometimes) and methods in classes but not methods in methods (local > methods). > Not only does this make for a lot of accidental complexity in specification, > implementation, and user's brains, but it means every feature interact with > this complexity. Nested records are implicitly static, but this meant that in > 14 we can't have nested records in non-static classes, because, non-static > classes can't have static members. (Yes, this could be fixed; hold your "why > don't you just" suggestions.) And we borked up the implementation of local > records the first time around, where they accidentally capture effectively > final locals, which they shouldn't -- because we'd never really charted the > "static local class" territory, and got it wrong the first time. (Yes, this can > be fixed too, and will be before 14 goes out.) > So, I'd like to propose a simpler, general story of nesting (which is consistent > with the ad-hoc rubbish we have) which we can get to in stages. The purpose of > this mail is to discuss the model; in what increments we get there is a > separate story. > Goals: > - Anything (class, interface, record, enum, method) can be nested in anything; > - Some things are always static (enums, records, interfaces) when nested; the > rest can be made static when desired; > - The rule about "no static members in nonstatic nested classes" has to go; > - Rules about whether members / locals from enclosing contexts can be specified > in a single place, using local reasoning. > The core of this is coming to an understanding of what "static" means. When > construct X nests in Y (whether X and Y are classes, methods, interfaces, etc), > for "X" to be "static" means that nesting is being used purely for purposes of > namespacing, and not for purposes of having access to names (locals or > nonstatic class members) from enclosing constructs. > Unfortunately all the terms we might use for whether or not a symbol in an outer > construct can be used in a nested construct -- such as "accessible" -- are > overloaded with other meanings. For purposes of this discussion, let's call > this "capturable" (this is also overloaded, but less so.) Each construct (class > type or method) has two sets of names from outer constructs that are capturable > -- a _statically capturable_ set SC(X), and a _non-statically capturable_ set > NC(X). We can define capturability using local reasoning: > Base cases: > - Names of static members in X are in SC(X); > - Names of instance members of X (if X is a class) or effectively final locals > of X (if X is a method) are in NC(X); > Induction cases, where X is nested directly in Y: > - SC(Y) is in SC(X) > - If _X is not static_, then NC(Y) is in NC(X) > We then say that X can capture names in SC(X) and NC(X); all we need to compute > capturability is the capture sets of X's immediately enclosing construct, and > whether X is static or not in that construct (modulo shadowing etc.) > For the math-challenged, what this means is: > - A nested construct can access static members of all the enclosing constructs; > - A nested non-static construct can access instance members and effectively > final locals of all enclosing constructs, up until we hit a static construct, > and then capturing stops. (So if Z is nested in Y is nested in static X, Z can > access instance members / eff final locals of Y and X but not anything > non-static from outside of X.) > Note that this is consistent with what currently happens when X is a method as > well as a class type; static methods in a class "capture" the static members of > the enclosing class, and instance methods also capture the instance members of > the enclosing class -- and also consistent with capturing in lambdas and > anonymous classes, if we assume that these are always non-static constructs. > We then say enums, records, and interfaces are _always_ static when nested, > whether declared so or not, we eliminate the restriction about static members > in non-static nested classes (now that we have a clear semantics for them), and > allow local classes to be declared as static. (Eventually, we also relax the > restrictions about methods in methods, static or not.) > (Additionally, the model supports the notion of "static lambda" and "static > anonymous class" with obvious semantics (can't capture anything); we can decide > later whether adding this flexibility is worth the additional surface syntax.) > This is a strict superset of the status quo, and yields a more flexible and > regular language -- and hopefully a simpler spec (since so many of these cases > are specified as ad-hoc corner cases.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jan 7 21:31:38 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 7 Jan 2020 16:31:38 -0500 Subject: Towards cleaner nesting In-Reply-To: <2014421213.1189937.1578432463507.JavaMail.zimbra@u-pem.fr> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <2014421213.1189937.1578432463507.JavaMail.zimbra@u-pem.fr> Message-ID: Yes, good catch.? Type parameters for a class X or method X are in NC(X); done. On 1/7/2020 4:27 PM, Remi Forax wrote: > Hi Brian, > Nice sump-up, i like it very much. > > I believe we also need to think about type parameters, they are also > impacted by the nesting/static context so should be in NC(X). > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"amber-spec-experts" > *Envoy?: *Mardi 7 Janvier 2020 21:30:27 > *Objet: *Towards cleaner nesting > > Everything about nesting in Java is a mess. The terminology is a > mess (top level classes, nested classes, inner classes, local > classes, anonymous classes); the set of restrictions on what can > nest in what is ad-hoc (can have local classes but not local > interfaces; inner classes cannot have static members, including > static nested classes), and the set of rules about what must be, > can be, or cannot be static is also ad-hoc (nested classes can be > static or not, nested interfaces are implicitly static, but local > and anonymous classes may not be static, even though it might make > sense.)? On top of that, we can nest classes in methods > (sometimes) and methods in classes but not methods in methods > (local methods). > > Not only does this make for a lot of accidental complexity in > specification, implementation, and user's brains, but it means > every feature interact with this complexity.? Nested records are > implicitly static, but this meant that in 14 we can't have nested > records in non-static classes, because, non-static classes can't > have static members.? (Yes, this could be fixed; hold your "why > don't you just" suggestions.)? And we borked up the implementation > of local records the first time around, where they accidentally > capture effectively final locals, which they shouldn't -- because > we'd never really charted the "static local class" territory, and > got it wrong the first time.? (Yes, this can be fixed too, and > will be before 14 goes out.) > > So, I'd like to propose a simpler, general story of nesting (which > is consistent with the ad-hoc rubbish we have) which we can get to > in stages.? The purpose of this mail is to discuss the model; in > what increments we get there is a separate story. > > Goals: > ?- Anything (class, interface, record, enum, method) can be nested > in anything; > ?- Some things are always static (enums, records, interfaces) when > nested; the rest can be made static when desired; > ?- The rule about "no static members in nonstatic nested classes" > has to go; > ?- Rules about whether members / locals from enclosing contexts > can be specified in a single place, using local reasoning. > > The core of this is coming to an understanding of what "static" > means.? When construct X nests in Y (whether X and Y are classes, > methods, interfaces, etc), for "X" to be "static" means that > nesting is being used purely for purposes of namespacing, and not > for purposes of having access to names (locals or nonstatic class > members) from enclosing constructs. > > Unfortunately all the terms we might use for whether or not a > symbol in an outer construct can be used in a nested construct -- > such as "accessible" -- are overloaded with other meanings.? For > purposes of this discussion, let's call this "capturable" (this is > also overloaded, but less so.)? Each construct (class type or > method) has two sets of names from outer constructs that are > capturable -- a _statically capturable_ set SC(X), and a > _non-statically capturable_ set NC(X).? We can define > capturability using local reasoning: > > Base cases: > ?- Names of static members in X are in SC(X); > ?- Names of instance members of X (if X is a class) or effectively > final locals of X (if X is a method) are in NC(X); > > Induction cases, where X is nested directly in Y: > ?- SC(Y) is in SC(X) > ?- If _X is not static_, then NC(Y) is in NC(X) > > We then say that X can capture names in SC(X) and NC(X); all we > need to compute capturability is the capture sets of X's > immediately enclosing construct, and whether X is static or not in > that construct (modulo shadowing etc.) > > For the math-challenged, what this means is: > ?- A nested construct can access static members of all the > enclosing constructs; > ?- A nested non-static construct can access instance members and > effectively final locals of all enclosing constructs, up until we > hit a static construct, and then capturing stops.? (So if Z is > nested in Y is nested in static X, Z can access instance members / > eff final locals of Y and X but not anything non-static from > outside of X.) > > Note that this is consistent with what currently happens when X is > a method as well as a class type; static methods in a class > "capture" the static members of the enclosing class, and instance > methods also capture the instance members of the enclosing class > -- and also consistent with capturing in lambdas and anonymous > classes, if we assume that these are always non-static constructs. > > We then say enums, records, and interfaces are _always_ static > when nested, whether declared so or not, we eliminate the > restriction about static members in non-static nested classes (now > that we have a clear semantics for them), and allow local classes > to be declared as static.? (Eventually, we also relax the > restrictions about methods in methods, static or not.) > > (Additionally, the model supports the notion of "static lambda" > and "static anonymous class" with obvious semantics (can't capture > anything); we can decide later whether adding this flexibility is > worth the additional surface syntax.) > > This is a strict superset of the status quo, and yields a more > flexible and regular language -- and hopefully a simpler spec > (since so many of these cases are specified as ad-hoc corner cases.) > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amalloy at google.com Tue Jan 7 22:15:31 2020 From: amalloy at google.com (Alan Malloy) Date: Tue, 7 Jan 2020 14:15:31 -0800 Subject: Towards cleaner nesting In-Reply-To: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> Message-ID: Very clear and useful. I would love for the nesting story to get simpler. I've discovered a number of corner cases where I thought some construct would be legal but in fact wasn't (e.g. an inner class with a static constant), and what you describe sounds like it will make the language easier to understand. Two remarks. First, I'm not sure what you mean by "static lambda". Of course it is already possible for a lambda to be declared as a static member of some class (static Runnable r = () -> {};), so you must mean something else, but I don't know what that is. Do you imagine a special declaration mode for a lambda that asks the compiler to ensure that it captures nothing? Second, how do code-containing constructs which are not methods or classes fit into this? I have field initializers in mind, but maybe static/instance initializers are relevant too. Does it make sense to nest a method inside of an instance initializer? On Tue, Jan 7, 2020 at 12:31 PM Brian Goetz wrote: > Everything about nesting in Java is a mess. The terminology is a mess > (top level classes, nested classes, inner classes, local classes, anonymous > classes); the set of restrictions on what can nest in what is ad-hoc (can > have local classes but not local interfaces; inner classes cannot have > static members, including static nested classes), and the set of rules > about what must be, can be, or cannot be static is also ad-hoc (nested > classes can be static or not, nested interfaces are implicitly static, but > local and anonymous classes may not be static, even though it might make > sense.) On top of that, we can nest classes in methods (sometimes) and > methods in classes but not methods in methods (local methods). > > Not only does this make for a lot of accidental complexity in > specification, implementation, and user's brains, but it means every > feature interact with this complexity. Nested records are implicitly > static, but this meant that in 14 we can't have nested records in > non-static classes, because, non-static classes can't have static members. > (Yes, this could be fixed; hold your "why don't you just" suggestions.) > And we borked up the implementation of local records the first time around, > where they accidentally capture effectively final locals, which they > shouldn't -- because we'd never really charted the "static local class" > territory, and got it wrong the first time. (Yes, this can be fixed too, > and will be before 14 goes out.) > > So, I'd like to propose a simpler, general story of nesting (which is > consistent with the ad-hoc rubbish we have) which we can get to in stages. > The purpose of this mail is to discuss the model; in what increments we get > there is a separate story. > > Goals: > - Anything (class, interface, record, enum, method) can be nested in > anything; > - Some things are always static (enums, records, interfaces) when nested; > the rest can be made static when desired; > - The rule about "no static members in nonstatic nested classes" has to > go; > - Rules about whether members / locals from enclosing contexts can be > specified in a single place, using local reasoning. > > The core of this is coming to an understanding of what "static" means. > When construct X nests in Y (whether X and Y are classes, methods, > interfaces, etc), for "X" to be "static" means that nesting is being used > purely for purposes of namespacing, and not for purposes of having access > to names (locals or nonstatic class members) from enclosing constructs. > > Unfortunately all the terms we might use for whether or not a symbol in an > outer construct can be used in a nested construct -- such as "accessible" > -- are overloaded with other meanings. For purposes of this discussion, > let's call this "capturable" (this is also overloaded, but less so.) Each > construct (class type or method) has two sets of names from outer > constructs that are capturable -- a _statically capturable_ set SC(X), and > a _non-statically capturable_ set NC(X). We can define capturability using > local reasoning: > > Base cases: > - Names of static members in X are in SC(X); > - Names of instance members of X (if X is a class) or effectively final > locals of X (if X is a method) are in NC(X); > > Induction cases, where X is nested directly in Y: > - SC(Y) is in SC(X) > - If _X is not static_, then NC(Y) is in NC(X) > > We then say that X can capture names in SC(X) and NC(X); all we need to > compute capturability is the capture sets of X's immediately enclosing > construct, and whether X is static or not in that construct (modulo > shadowing etc.) > > For the math-challenged, what this means is: > - A nested construct can access static members of all the enclosing > constructs; > - A nested non-static construct can access instance members and > effectively final locals of all enclosing constructs, up until we hit a > static construct, and then capturing stops. (So if Z is nested in Y is > nested in static X, Z can access instance members / eff final locals of Y > and X but not anything non-static from outside of X.) > > Note that this is consistent with what currently happens when X is a > method as well as a class type; static methods in a class "capture" the > static members of the enclosing class, and instance methods also capture > the instance members of the enclosing class -- and also consistent with > capturing in lambdas and anonymous classes, if we assume that these are > always non-static constructs. > > We then say enums, records, and interfaces are _always_ static when > nested, whether declared so or not, we eliminate the restriction about > static members in non-static nested classes (now that we have a clear > semantics for them), and allow local classes to be declared as static. > (Eventually, we also relax the restrictions about methods in methods, > static or not.) > > (Additionally, the model supports the notion of "static lambda" and > "static anonymous class" with obvious semantics (can't capture anything); > we can decide later whether adding this flexibility is worth the additional > surface syntax.) > > This is a strict superset of the status quo, and yields a more flexible > and regular language -- and hopefully a simpler spec (since so many of > these cases are specified as ad-hoc corner cases.) > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jan 7 22:43:30 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 7 Jan 2020 17:43:30 -0500 Subject: Towards cleaner nesting In-Reply-To: References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> Message-ID: > Two remarks. First, I'm not sure what you mean by "static lambda". Of > course it is already possible for a lambda to be declared as a static > member of some class (static Runnable r = () -> {};), so you must mean > something else, but I don't know what that is. Do you imagine a > special declaration mode for a lambda that asks the compiler to ensure > that it captures nothing? Not that I am saying Java needs these, but ... Lambdas can capture effectively final locals; those that happen to not do so are translated differently (the instance is cached at the capture side, so have better performance characteristics.)? Thing is an example of the compiler _inferring_ that the lambda could have been "static" (captures nothing from its context).? But the flip side is where the author would _declare_ that the lambda _cannot_ capture anything. This guarantees the better translation, but also gets better type chekcing -- if the author mistakenly captures something, they get a compiler error, rather than slower performance.? So this is what I mean by a "static lambda" -- a lambda that is declare to _not be allowed_ to capture anything. It gets worse with anon classes, because we don't even do the inference and the better translation there when we can, and not capture the enclosing instance when it doesn't get used, causing more footprint and potentially unintended retention. > Second, how do code-containing constructs which are not methods or > classes fit into this? I have field initializers in mind, but maybe > static/instance initializers are relevant too. Does it make sense to > nest a method inside of an instance initializer? For purposes of the capturing rules, field initializers for { static, instance } fields should be treated as nested { static, instance } methods, and similar for { static, instance } initializers. The question of whether to _allow_ local methods in these things is a separate story; there is a pro (consistency) and a con (ugh, really?) argument to be made there, but if we did, the capture rules would follow cleanly. (This rabbit hole goes deeper; in theory there could be local methods wherever a statement goes, such as: ??? int x = switch (z) { ??????? case 0 -> { ??????????? int f(z) { ... } ??????????? yield f(z); ??????? } ??? } ... and one of these fellows could go in a static initializer. Again, we get to decide how deep down the rabbit hole we want to go.) > > On Tue, Jan 7, 2020 at 12:31 PM Brian Goetz > wrote: > > Everything about nesting in Java is a mess.? The terminology is a > mess (top level classes, nested classes, inner classes, local > classes, anonymous classes); the set of restrictions on what can > nest in what is ad-hoc (can have local classes but not local > interfaces; inner classes cannot have static members, including > static nested classes), and the set of rules about what must be, > can be, or cannot be static is also ad-hoc (nested classes can be > static or not, nested interfaces are implicitly static, but local > and anonymous classes may not be static, even though it might make > sense.)? On top of that, we can nest classes in methods > (sometimes) and methods in classes but not methods in methods > (local methods). > > Not only does this make for a lot of accidental complexity in > specification, implementation, and user's brains, but it means > every feature interact with this complexity.? Nested records are > implicitly static, but this meant that in 14 we can't have nested > records in non-static classes, because, non-static classes can't > have static members.? (Yes, this could be fixed; hold your "why > don't you just" suggestions.)? And we borked up the implementation > of local records the first time around, where they accidentally > capture effectively final locals, which they shouldn't -- because > we'd never really charted the "static local class" territory, and > got it wrong the first time.? (Yes, this can be fixed too, and > will be before 14 goes out.) > > So, I'd like to propose a simpler, general story of nesting (which > is consistent with the ad-hoc rubbish we have) which we can get to > in stages.? The purpose of this mail is to discuss the model; in > what increments we get there is a separate story. > > Goals: > ?- Anything (class, interface, record, enum, method) can be nested > in anything; > ?- Some things are always static (enums, records, interfaces) when > nested; the rest can be made static when desired; > ?- The rule about "no static members in nonstatic nested classes" > has to go; > ?- Rules about whether members / locals from enclosing contexts > can be specified in a single place, using local reasoning. > > The core of this is coming to an understanding of what "static" > means.? When construct X nests in Y (whether X and Y are classes, > methods, interfaces, etc), for "X" to be "static" means that > nesting is being used purely for purposes of namespacing, and not > for purposes of having access to names (locals or nonstatic class > members) from enclosing constructs. > > Unfortunately all the terms we might use for whether or not a > symbol in an outer construct can be used in a nested construct -- > such as "accessible" -- are overloaded with other meanings.? For > purposes of this discussion, let's call this "capturable" (this is > also overloaded, but less so.)? Each construct (class type or > method) has two sets of names from outer constructs that are > capturable -- a _statically capturable_ set SC(X), and a > _non-statically capturable_ set NC(X).? We can define > capturability using local reasoning: > > Base cases: > ?- Names of static members in X are in SC(X); > ?- Names of instance members of X (if X is a class) or effectively > final locals of X (if X is a method) are in NC(X); > > Induction cases, where X is nested directly in Y: > ?- SC(Y) is in SC(X) > ?- If _X is not static_, then NC(Y) is in NC(X) > > We then say that X can capture names in SC(X) and NC(X); all we > need to compute capturability is the capture sets of X's > immediately enclosing construct, and whether X is static or not in > that construct (modulo shadowing etc.) > > For the math-challenged, what this means is: > ?- A nested construct can access static members of all the > enclosing constructs; > ?- A nested non-static construct can access instance members and > effectively final locals of all enclosing constructs, up until we > hit a static construct, and then capturing stops.? (So if Z is > nested in Y is nested in static X, Z can access instance members / > eff final locals of Y and X but not anything non-static from > outside of X.) > > Note that this is consistent with what currently happens when X is > a method as well as a class type; static methods in a class > "capture" the static members of the enclosing class, and instance > methods also capture the instance members of the enclosing class > -- and also consistent with capturing in lambdas and anonymous > classes, if we assume that these are always non-static constructs. > > We then say enums, records, and interfaces are _always_ static > when nested, whether declared so or not, we eliminate the > restriction about static members in non-static nested classes (now > that we have a clear semantics for them), and allow local classes > to be declared as static.? (Eventually, we also relax the > restrictions about methods in methods, static or not.) > > (Additionally, the model supports the notion of "static lambda" > and "static anonymous class" with obvious semantics (can't capture > anything); we can decide later whether adding this flexibility is > worth the additional surface syntax.) > > This is a strict superset of the status quo, and yields a more > flexible and regular language -- and hopefully a simpler spec > (since so many of these cases are specified as ad-hoc corner cases.) > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 8 20:27:54 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 8 Jan 2020 15:27:54 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) Message-ID: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> In the past, we've gone around a few times on nullability and pattern matching.? Back when we were enamored of `T?` types over in Valhalla land, we tentatively landed on using `T?` also for nullable type patterns.? But the bloom came off that rose pretty quickly, and Valhalla is moving away from it, and that makes it far less attractive in this context. There are a number of tangled concerns that we've tried a few times to unknot: ?- Construct nullability.? Constructs to which we want to add pattern awareness (instanceof, switch) already have their own opinion about nulls.? Instanceof always says false when presented with a null, and switch always NPEs. ?- Pattern nullability.? Some patterns clearly would never match null (deconstruction patterns), whereas others (an "any" pattern, and surely the `null` constant pattern, if there was one) might make sense to match null. ?- Nesting vs top-level.? Most of the time, we don't want to match null at the top level, but frequently in a nested position we do. This conflicts with... ?- Totality vs partiality.? When a pattern is partial on the operand type (e.g., `case String` when the operand of switch is `Object`), it is almost never the case we want to match null (well, except for the `null` constant pattern), whereas when a pattern is total on the operand type (e.g., `case Object` in the same example), it is more justifiable to match null. ?- Refactoring friendliness.? There are a number of cases that we would like to freely refactor back and forth (e.g., if-instanceof chain vs pattern switch).? In particular, refactoring a switch on nested patterns to a nested switch (case Foo(T t), case Foo(U u) to a nested switch on T and U) is problematic under some of the interpretations of nested patterns. ?- Inference.? It would be nice if a `var` pattern were simply inference for a type pattern, rather than some possibly-non-denotable union. (Both Scala and C# treat these differently, which means you have to choose between type inference and the desired semantics; I don't want to put users in the position of making this choice.) Let's try (again) to untangle these.? A compelling example is this one: ??? Box box; ??? switch (box) { ??????? case Box(Chocolate c): ??????? case Box(Frog f): ??????? case Box(var o): ??? } It would be highly confusing and error-prone for either of the first two patterns to match Box(null) -- given that Chocolate and Frog have no type relation (ok, maybe they both implement `Edible`), it should be perfectly safe to reorder the two.? But, because the last pattern is so obviously total on boxes, it is quite likely that what the author wants is to match all remaining boxes, including those that contain null. (Further, it would be super-bad if there were _no_way to say "Match any Box, even if it contains null.? While one might think this could be repaired with OR patterns, imagine that `Box` had N components -- we'd need to OR together 2^n patterns, with complex merging, to express all the possible combinations of nullity.) Scala and C# took the path of saying that "var" patterns are not just type inference, they are "any" patterns -- so `Box(Object o)` matches boxes containing a non-null payload, where `Box(var o)` matches all boxes.? I find this choice to be both questionable (the story that `var` is just inference is nice) and also that it puts users in the position of having to choose between the semantics they want and being explicit about types.? I see the expedience of it, but I do not think this is the right answer for Java. In the previous round, we posited that there were _type patterns_(denoted `T t`) and _nullable type patterns_(denoted `T? t`), which had the advantage that you could be explicit about what you wanted (nulls or not), and which was sort of banking on Valhalla plunking for the `T? ` notation.? But without that, only having `T?` in patterns, and no where else, will stick out like a sore thumb. There are many ways to denote "T or null", of course: ?- Union types: `case (T|Null) t` ?- OR patterns: `case (T t) | (Null t)`, or `case (T t) | (null t)` (the former is a union with a null TYPE pattern, the latter with a null CONSTANT pattern) ?- Merging/fallthrough: `case T t, Null t` ?- Some way to spell "nullable T": `case T? t`, `case nullable T t`, `case T|null t` But, I don't see any of these as being all that attractive in the Box case, when the most likely outcome is that the user wants the last case to match all boxes. Here's a scheme that I think is workable, which we hovered near sometime in the past, and which I want to go back to.? We'll start with the observation that `instanceof` and `switch` are currently hostile to nulls (instanceof says false, switch throws, and probably in the future, let/bind will do so also.) ?- We accept that some constructs may have legacy hostility to nulls (but, see below for a possible relaxation); ?- There are no "nullable type patterns", just type patterns; - Type patterns that are _total_ on their target operand (`case T` on an operand of type `U`, where `U <: T`) match null, and non-total type patterns do not. ?- Var patterns can be considered "just type inference" and will mean the same thing as a type pattern for the inferred type. In this world, the patterns that match null (if the construct allows it through) are `case null` and the total patterns -- which could be written `var x` (and maybe `_`, or maybe not), or `Object x`, or even a narrower type if the operand type is narrower. In our Box example, this means that the last case (whether written as `Box(var o)` or `Box(Object o)`) matches all boxes, including those containing null (because the nested pattern is total on the nested operand), but the first two cases do not. An objection raised against this scheme earlier is that readers will have to look at the declaration site of the pattern to know whether the nested pattern is total. This is a valid concern (to be traded off against the other valid concerns), but this does not seem so bad in practice to me -- it will be common to use var or other broad type, in which case it will be obvious.) One problem with this interpretation is that we can't trivially refactor from ??? switch (o) { ??????? case Box(Chocolate c): ??????? case Box(Frog f): ??????? case Box(var o): ??? } to ??? switch (o) { ??????? case Box(var contents): ??????????? switch (contents) { ??????????????? case Chocolate c: ??????????????? case Frog f: ??????????????? case Object o: ??????????? } ??????? } ??? } because the inner `switch(contents)` would NPE, because switch is null-hostile.? Instead, the user would explicitly have to do an `if (contents == null)` test, and, if the intent was to handle null in the same way as the bottom case, some duplication of code would be needed.? This is irritating, but I don't think it is disqualifying -- it is in the same category of null irritants that we have throughout the language. Similarly, we lose the pleasing decomposition that the nested pattern `P(Q)` is the same pattern as `P(alpha) & alpha instanceof Q` when P's 1st component might be null and the pattern Q is total -- because of the existing null-hostility of `instanceof`.? (This is not unlike the complaint that Optional doesn't follow the monad law, with a similar consequence -- and a similar justification.) So, summary: ?- the null constant pattern matches null; ?- "any" patterns match null; ?- A total type pattern is an "any" pattern; ?- var is just type inference; ?- no other patterns match null; ?- existing constructs retain their existing null behaviors. I'll follow up with a separate message about switch null-hostility. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 8 20:55:38 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 8 Jan 2020 15:55:38 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> Message-ID: Assuming you are happy with the plan in the previous message, let's move on to the ... SPECIAL BONUS ROUND -- POSSIBLY LESS NULL-HOSTILE SWITCHES (If you are not happy with the previous mail, please direct your comments to that first; this is purely a maybe-add-on to the previous.? Also, remember that patterns in switch is not coming in the next round, but the following one.) In the past, when we tried to relax the null-hostility to switch, we got some pushback along the lines of "Please don't make me grovel through all the cases to determine if a switch is null-friendly or null-hostile."? Making the user do an O(n) analysis would be bad here, but maybe an O(1) analysis is acceptable.? (We could have an alternate name for switch (`switch-nullable`), and this would address the problem (at the cost of, arguably, creating a new problem), but I'd like something a little more general.) Let's start with `case null`.? What if you were allowed to say `case null` in a switch, and the switch would do the obvious thing? ??? switch (o) { ??????? case null -> System.out.println("Ugh, null"); ??????? case Object o -> System.out.println("Yay, non-null: " + o); ??? } I don't think anyone would argue that it is all that confusing that this switch can deal with null inputs; the proximity of `case null` to the `switch` makes it pretty clear that we want to handle nulls.? (We could restrict this to the top of the switch, though that would be an exception to the general rule about pattern domination, though an acceptable one.) OK, now what about at the other end of the switch?? What if the last pattern is total (say, an any pattern.)? Is it also reasonable for that to match null? ??? switch (o) { ??????? case String s: ... ??????? case _: ... ??? } Is it reasonable for the last line to match null?? After all, we're saying "everything".? (For now, please ignore any latent assumptions that the last line means the same thing as "default".) I realize that there may be mixed opinions here, but what I'm getting at is: what if the reader only had to look at the first and last case to determine how nulls were handled in a switch? Would the benefits of uniform treatment of nulls in pattern matching (eliminating the attendant refactoring anomalies) outweigh the fact that users have to make a slight shift in their thinking about "switch is always null-hostile"?? (Note that there is no actual code compatibility issue; this is all mental-model compatibility.) The key idea here is to shift our orientation from "switch is null hostile" to "matching the `default` clause of a switch is null-hostile" -- and saying that non-total switches get an implicit default clause (just as we insert an extra throwing default clause into total switch expressions, just in case the classfiles change in an incompatible way between compilation and runtime.) So, if there is a `case null`, or the last clause is a total pattern, it gets the null; otherwise, there is either an explicit or implicit default clause, which is null-hostile. The main costs are two: ?- a `default` is not the same as `case _` or `case var x` or `case Object x`; ?- the mental model that switches are null-hostile needs to be shifted to "switch defaults are null-hostile." These are not trivial costs, but neither are the benefits.? I don't think its a slam-dunk either way; it's really a question of whether we want to spend some of our "surprise the user" budget on making the language more regular. Note that whichever way we go here, has no effect on the semantics outlined in the previous mail. On 1/8/2020 3:27 PM, Brian Goetz wrote: > In the past, we've gone around a few times on nullability and pattern > matching.? Back when we were enamored of `T?` types over in Valhalla > land, we tentatively landed on using `T?` also for nullable type > patterns.? But the bloom came off that rose pretty quickly, and > Valhalla is moving away from it, and that makes it far less attractive > in this context. > > There are a number of tangled concerns that we've tried a few times to > unknot: > > ?- Construct nullability.? Constructs to which we want to add pattern > awareness (instanceof, switch) already have their own opinion about > nulls.? Instanceof always says false when presented with a null, and > switch always NPEs. > > ?- Pattern nullability.? Some patterns clearly would never match null > (deconstruction patterns), whereas others (an "any" pattern, and > surely the `null` constant pattern, if there was one) might make sense > to match null. > > ?- Nesting vs top-level.? Most of the time, we don't want to match > null at the top level, but frequently in a nested position we do. This > conflicts with... > > ?- Totality vs partiality.? When a pattern is partial on the operand > type (e.g., `case String` when the operand of switch is `Object`), it > is almost never the case we want to match null (well, except for the > `null` constant pattern), whereas when a pattern is total on the > operand type (e.g., `case Object` in the same example), it is more > justifiable to match null. > > ?- Refactoring friendliness.? There are a number of cases that we > would like to freely refactor back and forth (e.g., if-instanceof > chain vs pattern switch).? In particular, refactoring a switch on > nested patterns to a nested switch (case Foo(T t), case Foo(U u) to a > nested switch on T and U) is problematic under some of the > interpretations of nested patterns. > > ?- Inference.? It would be nice if a `var` pattern were simply > inference for a type pattern, rather than some possibly-non-denotable > union.? (Both Scala and C# treat these differently, which means you > have to choose between type inference and the desired semantics; I > don't want to put users in the position of making this choice.) > > > Let's try (again) to untangle these.? A compelling example is this one: > > ??? Box box; > ??? switch (box) { > ??????? case Box(Chocolate c): > ??????? case Box(Frog f): > ??????? case Box(var o): > ??? } > > It would be highly confusing and error-prone for either of the first > two patterns to match Box(null) -- given that Chocolate and Frog have > no type relation (ok, maybe they both implement `Edible`), it should > be perfectly safe to reorder the two.? But, because the last pattern > is so obviously total on boxes, it is quite likely that what the > author wants is to match all remaining boxes, including those that > contain null. (Further, it would be super-bad if there were _no_way to > say "Match any Box, even if it contains null.? While one might think > this could be repaired with OR patterns, imagine that `Box` had N > components -- we'd need to OR together 2^n patterns, with complex > merging, to express all the possible combinations of nullity.) > > Scala and C# took the path of saying that "var" patterns are not just > type inference, they are "any" patterns -- so `Box(Object o)` matches > boxes containing a non-null payload, where `Box(var o)` matches all > boxes.? I find this choice to be both questionable (the story that > `var` is just inference is nice) and also that it puts users in the > position of having to choose between the semantics they want and being > explicit about types.? I see the expedience of it, but I do not think > this is the right answer for Java. > > > In the previous round, we posited that there were _type > patterns_(denoted `T t`) and _nullable type patterns_(denoted `T? t`), > which had the advantage that you could be explicit about what you > wanted (nulls or not), and which was sort of banking on Valhalla > plunking for the `T? ` notation.? But without that, only having `T?` > in patterns, and no where else, will stick out like a sore thumb. > > There are many ways to denote "T or null", of course: > > ?- Union types: `case (T|Null) t` > ?- OR patterns: `case (T t) | (Null t)`, or `case (T t) | (null t)` > (the former is a union with a null TYPE pattern, the latter with a > null CONSTANT pattern) > ?- Merging/fallthrough: `case T t, Null t` > ?- Some way to spell "nullable T": `case T? t`, `case nullable T t`, > `case T|null t` > > But, I don't see any of these as being all that attractive in the Box > case, when the most likely outcome is that the user wants the last > case to match all boxes. > > > Here's a scheme that I think is workable, which we hovered near > sometime in the past, and which I want to go back to. We'll start with > the observation that `instanceof` and `switch` are currently hostile > to nulls (instanceof says false, switch throws, and probably in the > future, let/bind will do so also.) > > ?- We accept that some constructs may have legacy hostility to nulls > (but, see below for a possible relaxation); > ?- There are no "nullable type patterns", just type patterns; > - Type patterns that are _total_ on their target operand (`case T` on > an operand of type `U`, where `U <: T`) match null, and non-total type > patterns do not. > ?- Var patterns can be considered "just type inference" and will mean > the same thing as a type pattern for the inferred type. > > In this world, the patterns that match null (if the construct allows > it through) are `case null` and the total patterns -- which could be > written `var x` (and maybe `_`, or maybe not), or `Object x`, or even > a narrower type if the operand type is narrower. > > In our Box example, this means that the last case (whether written as > `Box(var o)` or `Box(Object o)`) matches all boxes, including those > containing null (because the nested pattern is total on the nested > operand), but the first two cases do not. > > An objection raised against this scheme earlier is that readers will > have to look at the declaration site of the pattern to know whether > the nested pattern is total. This is a valid concern (to be traded off > against the other valid concerns), but this does not seem so bad in > practice to me -- it will be common to use var or other broad type, in > which case it will be obvious.) > > One problem with this interpretation is that we can't trivially > refactor from > > ??? switch (o) { > ??????? case Box(Chocolate c): > ??????? case Box(Frog f): > ??????? case Box(var o): > ??? } > > to > > ??? switch (o) { > ??????? case Box(var contents): > ??????????? switch (contents) { > ??????????????? case Chocolate c: > ??????????????? case Frog f: > ??????????????? case Object o: > ??????????? } > ??????? } > ??? } > > because the inner `switch(contents)` would NPE, because switch is > null-hostile.? Instead, the user would explicitly have to do an `if > (contents == null)` test, and, if the intent was to handle null in the > same way as the bottom case, some duplication of code would be > needed.? This is irritating, but I don't think it is disqualifying -- > it is in the same category of null irritants that we have throughout > the language. > > Similarly, we lose the pleasing decomposition that the nested pattern > `P(Q)` is the same pattern as `P(alpha) & alpha instanceof Q` when P's > 1st component might be null and the pattern Q is total -- because of > the existing null-hostility of `instanceof`.? (This is not unlike the > complaint that Optional doesn't follow the monad law, with a similar > consequence -- and a similar justification.) > > So, summary: > ?- the null constant pattern matches null; > ?- "any" patterns match null; > ?- A total type pattern is an "any" pattern; > ?- var is just type inference; > ?- no other patterns match null; > ?- existing constructs retain their existing null behaviors. > > > I'll follow up with a separate message about switch null-hostility. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Wed Jan 8 23:13:05 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 8 Jan 2020 15:13:05 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> Message-ID: <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> On Jan 8, 2020, at 12:27 PM, Brian Goetz wrote: > > So, summary: > - the null constant pattern matches null; > - "any" patterns match null; > - A total type pattern is an "any" pattern; > - var is just type inference; > - no other patterns match null; > - existing constructs retain their existing null behaviors. FWIW, I love this, because (a) it gives null a little niche at the top (case ?any") and bottom (case null) of the lattice of patterns, and (b) it collapses two useful surface forms of patterns into one essential kind (?any?). I think, and have argued elsewhere, that although type inference in general risks being a confusing ?action at a distance? phenomenon, this use of total type patterns is constrained enough to be easily readable in normal usage. (Hint: The total type pattern is constrained to be the last one. So the type, if not ?var?, is really just a way of documenting the switch type.) ? John From john.r.rose at oracle.com Wed Jan 8 23:26:26 2020 From: john.r.rose at oracle.com (John Rose) Date: Wed, 8 Jan 2020 15:26:26 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> Message-ID: <37BF7825-07B1-48E2-906E-42B5B1BCFF88@oracle.com> I think switch should be optionally null-friendly, and the path you?ve sketched is a very simple way to get that outcome. On Jan 8, 2020, at 12:55 PM, Brian Goetz wrote: > > The main costs are two: > - a `default` is not the same as `case _` or `case var x` or `case Object x`; It would be nice to say ?default is sugar for case any? but it?s just ?nice?, not essential to the story of switch. As a legacy construct ?default? is always going to be an outlier. For example, it can be reordered with respect to other cases in legacy switches at least. I think it is a legitimate move to ?blame default? for null hostility, as part of the legacy quirks. I *don?t* think this is in any way harder to learn or use than the alternatives. > - the mental model that switches are null-hostile needs to be shifted to "switch defaults are null-hostile.? Among the various mental model shifts for switch this is trivial. Let?s not obsess on it. The ?switch doesn?t handle null? meme is no more sacred than ?switch only applies to certain types? or ?switch only applies to constant expressions?. All such memes can be readily discarded under the rubric of ?Java is now removing previous limitations on existing constructs.? Anybody who welcomes this extension to Java should welcome null handling along with all the other extensions. Yes, null is a singular value, but it is not always an inappropriate one. And a general-purpose construct (like switch or instanceof) should not make policy decisions like ?we don?t serve nulls here?. (Throwing NPE on an actual method or field access is deeper than mere policy: In that case there?s really no method or field to observe through the null. Throwing NPE on the mere examination of a null, a la Objects.requireNonNull, is the enforcement of a non-essential policy. That?s not a good job for Java syntax.) The benefit of moving the null-hostility from ?switch? down to ?default? (?blame it on default?) is that switch becomes competent to deal, uniformly, with all values expressible in Java. Instead of all values expressible in Java except null. ? John From brian.goetz at oracle.com Wed Jan 8 23:55:10 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 8 Jan 2020 18:55:10 -0500 Subject: [records] Record updates for Preview/2 Message-ID: We're gathering a list of what non-bugfix things we want to do for the second preview of records.? So far, on my list, I have: 1.? Revisiting the j.l.Record supertype.? We want to support inline records when we have inline types.? Until now, we've been working on the assumption in Valhalla that inline classes can only extend interfaces, not abstract classes, so it was suggested that Record should be an interface.? However, that topic in Valhalla is under reconsideration, and I want to wait until that discussion plays out before making any changes here. It has also been pointed out that the name Record is somewhat clash-y.? I'm not really willing to pick a lousy name just to reduce the chance of clashes, but I might be OK with a name like RecordClass.? (Bikeshed alert: if you want to discuss any of these topics, please start a new thread; this one is about curating a to-do list.) 2.? Accessibility of mandated members.? Remi noted that the requirement that the mandated record members be public, even for non-public classes, was weird.? But, at the time, the spec was in a state that trying to revisit this was impractical -- Gavin has now left the spec in a much cleaner place, and so it is reasonable to reopen this discussion.? The leading alternate candidate is to propagate the accessibility of the record to its mandated members (public for public, etc), but still require the author to say what they mean. 3.? Nesting considerations.? In 14 we will fix the issues surrounding local records, but we still ban nested records in non-static classes.? We should fix this -- by dropping the restriction on static members in inner classes -- and then bring records, enums, and interfaces to parity (allowing local and nested flavors of all, all implicitly static.) 4.? Abstract records.? Several people have asked "what about abstract records"; while these are theoretically possible, there are some complications that I think are best left for treating these as a later addition if needed.? But, for the record, here are some thoughts from the last time I looked into this. Given that records are nominal tuples, the notion of "width subtyping" comes immediately to mind.? So, let's go with that for a moment: you could say ??? abstract record A(int a, int b) { } and ??? record B(int a, int b, int c) extends A { } and there is a natural width subtyping relationship.? We don't have problems with the equality contract because the abstract class leaves equals() abstract. But, this is a story that is likely to not be entirely satisfactory.? Do we require that the state of A form a prefix for the state of B?? This may not be the API people want.? Do we require that super(a,b) be passed through unchanged?? The constraints on the subclass in this model get, constraining. What if there were a more flexible relationship: ??? record B(int a, int b, int c) extends A(a, b) { } Now, there's more flexibility, at the cost of a new "extends" construct.? And what if you want to fix some field of A as a constant: ??? record iload2_bytecode() extends bytecode(0x1c) { } These all seems like reasonable things to want when you get into abstract records ... but they all start to push on the "records imperative".? So for now, we're doing nothing, until we have a better story for what we actually want to do. 5.? Deconstruction patterns.? Yes, records should be deconstructible with deconstruction patterns. Anything else, that we've already discussed that I left out? -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Thu Jan 9 00:17:04 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 8 Jan 2020 17:17:04 -0700 Subject: Towards cleaner nesting In-Reply-To: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> Message-ID: <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> > On Jan 7, 2020, at 1:30 PM, Brian Goetz wrote: > > Goals: > - Anything (class, interface, record, enum, method) can be nested in anything; > - Some things are always static (enums, records, interfaces) when nested; the rest can be made static when desired; FWIW, I think it's helpful for case analysis to flatten the list of things that can be nested/contain nested things as follows: - top-level/static classes - inner classes* (I always forget whether this includes local classes, but turns out it does) - interfaces - records - enums - annotation types - static methods/initializers - instance methods/local methods/constructors/instance initializers* The proposal is that each one of these can be nested in any of these. Two of these eight are marked with asterisks because their bodies can reference enclosing non-static variables and type variables. All the rest are "static declarations". Annotation types currently have some restrictions on methods that probably ought to be relaxed for uniformity? Two things this presentation leaves off of the list: instance/local variables, and static variables. These both continue to have ad hoc nesting constraints, probably for good reason: - instance/local variables cannot be nested in interfaces, records, or annotation types - static variables cannot be nested in instance/local methods or static methods (Some other languages support the second one, which I always find weird; on the other hand, once you have static classes nested in methods, you effectively have the same thing.) > Each construct (class type or method) has two sets of names from outer constructs that are capturable -- a _statically capturable_ set SC(X), and a _non-statically capturable_ set NC(X). We can define capturability using local reasoning: > > Base cases: > - Names of static members in X are in SC(X); > - Names of instance members of X (if X is a class) or effectively final locals of X (if X is a method) are in NC(X); > > Induction cases, where X is nested directly in Y: > - SC(Y) is in SC(X) > - If _X is not static_, then NC(Y) is in NC(X) > > We then say that X can capture names in SC(X) and NC(X); all we need to compute capturability is the capture sets of X's immediately enclosing construct, and whether X is static or not in that construct (modulo shadowing etc.) > > For the math-challenged, what this means is: > - A nested construct can access static members of all the enclosing constructs; > - A nested non-static construct can access instance members and effectively final locals of all enclosing constructs, up until we hit a static construct, and then capturing stops. (So if Z is nested in Y is nested in static X, Z can access instance members / eff final locals of Y and X but not anything non-static from outside of X.) Here's the JLS rule, from 6.5.6.1, that we're enhancing: "If an expression name consists of a single Identifier, then there must be exactly one declaration denoting either a local variable, formal parameter, or field in scope at the point at which the Identifier occurs. Otherwise, a compile-time error occurs." "If the declaration denotes an instance variable (?8.3.1.1), the expression name must appear within an instance method (?8.4.3.2), instance variable initializer (?8.3.2), instance initializer (?8.6), or constructor (?8.8). If the expression name appears within a class method, class variable initializer, or static initializer (?8.7), then a compile-time error occurs." Could be modified to something like: "If the declaration denotes a local variable, formal parameter, or instance variable, then the expression name must not appear within a static type declaration, static method, static field initializer, or static initializer, unless that declaration also encloses the variable declaration. Otherwise, a compile-time error occurs." From forax at univ-mlv.fr Thu Jan 9 00:50:20 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 9 Jan 2020 01:50:20 +0100 (CET) Subject: [records] Record updates for Preview/2 In-Reply-To: References: Message-ID: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> Still on my to-do list: - @Deprecated on record components - change the bootstrap method protocol (a little bit) - equals/hashCode implementation are currently slooooow - the bytecode representation can be more lightweight - separate the bootstrap entry point used by Java from the one that can be used by other JVM languages so the Java bootstrap entry point can use the RecordComponent attribute without mandating this attribute to be present if another language just want an implementation of equals/hashCode/toString to be provided by the JDK. and as you already mentioned it, the visibility of members. R?mi De: "Brian Goetz" ?: "amber-spec-experts" Envoy?: Jeudi 9 Janvier 2020 00:55:10 Objet: [records] Record updates for Preview/2 BQ_BEGIN We're gathering a list of what non-bugfix things we want to do for the second preview of records. So far, on my list, I have: 1. Revisiting the j.l.Record supertype. We want to support inline records when we have inline types. Until now, we've been working on the assumption in Valhalla that inline classes can only extend interfaces, not abstract classes, so it was suggested that Record should be an interface. However, that topic in Valhalla is under reconsideration, and I want to wait until that discussion plays out before making any changes here. It has also been pointed out that the name Record is somewhat clash-y. I'm not really willing to pick a lousy name just to reduce the chance of clashes, but I might be OK with a name like RecordClass. (Bikeshed alert: if you want to discuss any of these topics, please start a new thread; this one is about curating a to-do list.) 2. Accessibility of mandated members. Remi noted that the requirement that the mandated record members be public, even for non-public classes, was weird. But, at the time, the spec was in a state that trying to revisit this was impractical -- Gavin has now left the spec in a much cleaner place, and so it is reasonable to reopen this discussion. The leading alternate candidate is to propagate the accessibility of the record to its mandated members (public for public, etc), but still require the author to say what they mean. 3. Nesting considerations. In 14 we will fix the issues surrounding local records, but we still ban nested records in non-static classes. We should fix this -- by dropping the restriction on static members in inner classes -- and then bring records, enums, and interfaces to parity (allowing local and nested flavors of all, all implicitly static.) 4. Abstract records. Several people have asked "what about abstract records"; while these are theoretically possible, there are some complications that I think are best left for treating these as a later addition if needed. But, for the record, here are some thoughts from the last time I looked into this. Given that records are nominal tuples, the notion of "width subtyping" comes immediately to mind. So, let's go with that for a moment: you could say abstract record A(int a, int b) { } and record B(int a, int b, int c) extends A { } and there is a natural width subtyping relationship. We don't have problems with the equality contract because the abstract class leaves equals() abstract. But, this is a story that is likely to not be entirely satisfactory. Do we require that the state of A form a prefix for the state of B? This may not be the API people want. Do we require that super(a,b) be passed through unchanged? The constraints on the subclass in this model get, constraining. What if there were a more flexible relationship: record B(int a, int b, int c) extends A(a, b) { } Now, there's more flexibility, at the cost of a new "extends" construct. And what if you want to fix some field of A as a constant: record iload2_bytecode() extends bytecode(0x1c) { } These all seems like reasonable things to want when you get into abstract records ... but they all start to push on the "records imperative". So for now, we're doing nothing, until we have a better story for what we actually want to do. 5. Deconstruction patterns. Yes, records should be deconstructible with deconstruction patterns. Anything else, that we've already discussed that I left out? BQ_END -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Jan 9 00:50:26 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 9 Jan 2020 01:50:26 +0100 (CET) Subject: Abstract record In-Reply-To: References: Message-ID: <1421952085.17340.1578531026845.JavaMail.zimbra@u-pem.fr> abstract record is a big NO for me: - we already discussed about that and perhaps it should be in the JEP but because it's so easy to add a new record component to a record and the fact that we have default method in interfaces, there is no need of an abstract record. - it also means that you can inherits record components, so the reflection API has to be reworked to make the difference between the declared record components and the inherited ones. - and the concept of inheritance hierarchy is so overrated. The code is hard to read, hard to impossible to refactor. - extends + constant, it's reinventing the constructor initialization list of C++ with all its problems. and ironically, the value 0x1c will not be seen as a constant by the VM. So all the drawbacks of the C++ notation, no advantage. regards, R?mi > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Jeudi 9 Janvier 2020 00:55:10 > Objet: [records] Record updates for Preview/2 > We're gathering a list of what non-bugfix things we want to do for the second > preview of records. So far, on my list, I have: > 1. Revisiting the j.l.Record supertype. We want to support inline records when > we have inline types. Until now, we've been working on the assumption in > Valhalla that inline classes can only extend interfaces, not abstract classes, > so it was suggested that Record should be an interface. However, that topic in > Valhalla is under reconsideration, and I want to wait until that discussion > plays out before making any changes here. > It has also been pointed out that the name Record is somewhat clash-y. I'm not > really willing to pick a lousy name just to reduce the chance of clashes, but I > might be OK with a name like RecordClass. (Bikeshed alert: if you want to > discuss any of these topics, please start a new thread; this one is about > curating a to-do list.) > 2. Accessibility of mandated members. Remi noted that the requirement that the > mandated record members be public, even for non-public classes, was weird. But, > at the time, the spec was in a state that trying to revisit this was > impractical -- Gavin has now left the spec in a much cleaner place, and so it > is reasonable to reopen this discussion. The leading alternate candidate is to > propagate the accessibility of the record to its mandated members (public for > public, etc), but still require the author to say what they mean. > 3. Nesting considerations. In 14 we will fix the issues surrounding local > records, but we still ban nested records in non-static classes. We should fix > this -- by dropping the restriction on static members in inner classes -- and > then bring records, enums, and interfaces to parity (allowing local and nested > flavors of all, all implicitly static.) > 4. Abstract records. Several people have asked "what about abstract records"; > while these are theoretically possible, there are some complications that I > think are best left for treating these as a later addition if needed. But, for > the record, here are some thoughts from the last time I looked into this. > Given that records are nominal tuples, the notion of "width subtyping" comes > immediately to mind. So, let's go with that for a moment: you could say > abstract record A(int a, int b) { } > and > record B(int a, int b, int c) extends A { } > and there is a natural width subtyping relationship. We don't have problems with > the equality contract because the abstract class leaves equals() abstract. > But, this is a story that is likely to not be entirely satisfactory. Do we > require that the state of A form a prefix for the state of B? This may not be > the API people want. Do we require that super(a,b) be passed through unchanged? > The constraints on the subclass in this model get, constraining. > What if there were a more flexible relationship: > record B(int a, int b, int c) extends A(a, b) { } > Now, there's more flexibility, at the cost of a new "extends" construct. And > what if you want to fix some field of A as a constant: > record iload2_bytecode() extends bytecode(0x1c) { } > These all seems like reasonable things to want when you get into abstract > records ... but they all start to push on the "records imperative". So for now, > we're doing nothing, until we have a better story for what we actually want to > do. > 5. Deconstruction patterns. Yes, records should be deconstructible with > deconstruction patterns. > Anything else, that we've already discussed that I left out? -------------- next part -------------- An HTML attachment was scrubbed... URL: From peter.levart at gmail.com Thu Jan 9 07:52:20 2020 From: peter.levart at gmail.com (Peter Levart) Date: Thu, 9 Jan 2020 08:52:20 +0100 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> Message-ID: <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> Hi, On 1/9/20 12:13 AM, John Rose wrote: > On Jan 8, 2020, at 12:27 PM, Brian Goetz wrote: >> So, summary: >> - the null constant pattern matches null; >> - "any" patterns match null; >> - A total type pattern is an "any" pattern; >> - var is just type inference; >> - no other patterns match null; >> - existing constructs retain their existing null behaviors. > FWIW, I love this, because (a) it gives null a little niche > at the top (case ?any") and bottom (case null) of the lattice > of patterns, and (b) it collapses two useful surface forms > of patterns into one essential kind (?any?). > > I think, and have argued elsewhere, that although type > inference in general risks being a confusing ?action at a > distance? phenomenon, this use of total type patterns > is constrained enough to be easily readable in normal > usage. (Hint: The total type pattern is constrained to > be the last one. So the type, if not ?var?, is really just > a way of documenting the switch type.) > > ? John I'm just thinking if such arrangement allows expressing all possible situations that may arise in a switch. Imagine one wants to switch on (Object o) and have the following cases: - String: case 1 - CharSequence or null: case 2 - any other: case 3 How would one structure such switch? Would case 2 have to be split before and after case 1? Regards, Peter From brian.goetz at oracle.com Thu Jan 9 15:05:20 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 10:05:20 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> Message-ID: > I'm just thinking if such arrangement allows expressing all possible > situations that may arise in a switch. > > Imagine one wants to switch on (Object o) and have the following cases: > > - String: case 1 > - CharSequence or null: case 2 > - any other: case 3 > > How would one structure such switch? Would case 2 have to be split > before and after case 1? Probably, but there are still some open questions that might affect the answer. ?? Note this is not all that different from: ?- String: case 1 ?- CharSequence, or the constant string "Foo": case 2 ?- any other: case 3 The goal is not necessarily to be able to represent any chain of conditionals in switch, though of course we do not want to gratuitously make it difficult. As I mentioned in the "lots of lousy ways to say T or null" section, we can make this easier at the cost of greater complexity.? Type patterns have a binding variable; constant patterns (currently) do not.? If they did, you could say: ??? case String s: ... ??? case null x, CharSequence x: ... ??? case Object: ... But, this brings two kinds of complexity with it: binding merging, and constant patterns with binding variables, which are (mostly) useless except for corner cases like this. From brian.goetz at oracle.com Thu Jan 9 15:07:50 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 10:07:50 -0500 Subject: [records] Record updates for Preview/2 In-Reply-To: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> Message-ID: > - @Deprecated on record components Please outline what you think we want on a separate thread? > - change the bootstrap method protocol (a little bit) Let's start a thread on this too. > ? - equals/hashCode implementation are currently slooooow Can you qualify/quantify "slow"? > ? - the bytecode representation can be more lightweight > ? - separate the bootstrap entry point used by Java from the one that > can be used by other JVM languages > ??? so the Java bootstrap entry point can use the RecordComponent > attribute without mandating this attribute to > ??? be present if another language just want an implementation of > equals/hashCode/toString to be provided by the JDK. And expand this argument a bit.? (All on a separate thread.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 9 16:03:31 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 11:03:31 -0500 Subject: Towards cleaner nesting In-Reply-To: <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> Message-ID: <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> > FWIW, I think it's helpful for case analysis to flatten the list of things that can be nested/contain nested things as follows: > - top-level/static classes > - inner classes* (I always forget whether this includes local classes, but turns out it does) > - interfaces > - records > - enums > - annotation types > - static methods/initializers > - instance methods/local methods/constructors/instance initializers* > > The proposal is that each one of these can be nested in any of these. Of course, some of these don't make sense, such as "constructors in field initializers", so it's not quite as simple as that, but yes, we should seek to bust the _gratuitous_ restrictions. > Annotation types currently have some restrictions on methods that probably ought to be relaxed for uniformity? Not sure "for uniformity" is a good enough reason; for example, we don't allow default methods in annotations, and I don't see this as being a "gratuitous" restriction.? But, since annotations are interfaces, then we should probably allow them in all the nested positions where we allow interfaces (and like interfaces, they should always be implicitly static.) From forax at univ-mlv.fr Thu Jan 9 17:51:38 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 9 Jan 2020 18:51:38 +0100 (CET) Subject: [records] Record updates for Preview/2 In-Reply-To: References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> Message-ID: <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 9 Janvier 2020 16:07:50 > Objet: Re: [records] Record updates for Preview/2 >> - @Deprecated on record components > Please outline what you think we want on a separate thread? Currently you can not deprecate a record component unlike in Scala or in Kotlin. We can either allow @Deprecated or pretend that people will never make mistake, change their mind, specification will never change, etc How to fix the issue, - as Dmitry Bessonov said on the amber mailing list, the Deprecated annotation should list ElementType.RECORD_COMPONENT as possible target. - the JVMS should be changed to allow the attribute Deprecated on record_component. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 9 17:57:58 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 12:57:58 -0500 Subject: Component deprecation (was: [records] Record updates for Preview/2) In-Reply-To: <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> Message-ID: The Deprecated attribute is, effectively, Deprecated.? So I'm not interested in giving it any life support. However, it is reasonable to allow the @Deprecated annotation on record components.? And, you can apply it there now!? And if you do, it will get propagated to fields, accessors, and constructor parameters, and reflection will reflect the right thing -- because its an annotation. It is reasonable to add RECORD_COMPONENT to the target type, though not necessary.? This would cause it to be propagated through to the Record attribute, where reflection would report it.? I'm pretty not-interested in spending any spec or compiler effort on this, though.? Its an annotation; we support annotations. On 1/9/2020 12:51 PM, forax at univ-mlv.fr wrote: > > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"Remi Forax" > *Cc: *"amber-spec-experts" > *Envoy?: *Jeudi 9 Janvier 2020 16:07:50 > *Objet: *Re: [records] Record updates for Preview/2 > > > - @Deprecated on record components > > > Please outline what you think we want on a separate thread? > > > Currently you can not deprecate a record component unlike in Scala or > in Kotlin. > We can either allow @Deprecated or pretend that people will never make > mistake, change their mind, specification will never change, etc > > How to fix the issue, > - as Dmitry Bessonov said on the amber mailing list, the Deprecated > annotation should list ElementType.RECORD_COMPONENT as possible target. > - the JVMS should be changed to allow the attribute Deprecated on > record_component. > > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Thu Jan 9 23:21:30 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 9 Jan 2020 16:21:30 -0700 Subject: Towards cleaner nesting In-Reply-To: <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> Message-ID: <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> > On Jan 9, 2020, at 9:03 AM, Brian Goetz wrote: > >> FWIW, I think it's helpful for case analysis to flatten the list of things that can be nested/contain nested things as follows: >> - top-level/static classes >> - inner classes* (I always forget whether this includes local classes, but turns out it does) >> - interfaces >> - records >> - enums >> - annotation types >> - static methods/initializers >> - instance methods/local methods/constructors/instance initializers* >> >> The proposal is that each one of these can be nested in any of these. > > Of course, some of these don't make sense, such as "constructors in field initializers", so it's not quite as simple as that, but yes, we should seek to bust the _gratuitous_ restrictions. Yeah, thinking a bit more, I guess I'd refine my list like so: Type declarations: - top-level/static classes - inner classes* - interfaces - records - enums - annotation types Method declarations: - static methods - instance/local methods* - static initializers - instance initializers/constructors* Variable declarations (leaf nodes, not containers): - instance/local variables* - static variables And then "anything can be nested inside of anything" has the following exceptions: - interfaces can't directly contain instance initializers/constructors, instance/local variables, or inner classes (maybe) - records can't directly contain instance/local variables - annotation types can't directly contain things prohibited in interfaces, or instance/local methods (maybe) - method declarations of any kind can't directly contain static initializers, instance initializers/constructors, static methods (maybe), or static variables (maybe) So, not quite "anything goes", but a much shorter and more refined list of prohibitions than we had previously. From daniel.smith at oracle.com Thu Jan 9 23:42:22 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 9 Jan 2020 16:42:22 -0700 Subject: Component deprecation (was: [records] Record updates for Preview/2) In-Reply-To: References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> Message-ID: <6255AEE6-C814-4225-B076-EFE6A4929F93@oracle.com> > On Jan 9, 2020, at 10:51 AM, forax at univ-mlv.fr wrote: > > Currently you can not deprecate a record component unlike in Scala or in Kotlin. > We can either allow @Deprecated or pretend that people will never make mistake, change their mind, specification will never change, etc Setting aside implementation details, and whether there's any change to be made here: I'm curious about what you intend a deprecated record component to *mean*. How do you deprecate a single parameter of a constructor? A concrete example could be useful?I can believe there might be something there, but I'm struggling to see it. A related StackOverflow discussion about deprecated parameters/locals: https://stackoverflow.com/questions/14627313/what-does-deprecated-mean-on-method-parameters-and-local-variables From john.r.rose at oracle.com Fri Jan 10 00:57:15 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 16:57:15 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> Message-ID: <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> On Jan 8, 2020, at 11:52 PM, Peter Levart wrote: > > I'm just thinking if such arrangement allows expressing all possible situations that may arise in a switch. > > Imagine one wants to switch on (Object o) and have the following cases: > > - String: case 1 > - CharSequence or null: case 2 > - any other: case 3 > > How would one structure such switch? Would case 2 have to be split before and after case 1? Either, I think. Coder?s choice, based on taste, coding style norms, and the shape of the problem. Note that if ?case null? comes first, it?s really easy to tell that the switch accepts null, and that?s probably the right answer most of the time. But if ?case null? follows ?case String?, we still get the same decision tree (in a different lexical form), because ?case String?, being non-total (the switch is on ?Object o?), excludes null. For the same reason, ?case null? could also go *after* ?case CharSequence?. switch ((Object)o) { //OK placement// case null: return 2; case String: return 1; //OK placement// case null: return 2; case CharSequence: return 2; //OK placement// case null: return 2; case Object: //OK workaround// if (o == null) return 2; return 3; } Any of the individual ?//OK*//? lines could be uncommented to get the same decisions. As Brian said, since we don?t have plans to do T? patterns or binding merging, that?s as good as it gets. We want to avoid adding more machinery just to favor these use cases. IMO *simple* support for nulls is a glass 99% full, but clearly there?s a 1% effect from use case like these. Java is full of such 99% full features. We haven?t yet moved on to library-defined patterns (?static patterns?). To gaze a bit into the crystal ball: I think it?s likely that those will provide a way to express T? using a library API instead of a language primitive. We should defer building out that last 1% of null support either indefinitely, or until there is a way for libraries to define their own kinds of patterns. In the latter case the cost of adding a nullable pattern is likely to be lower than the alternatives on the table, since library changes are always cheaper than language changes. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 03:02:50 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 22:02:50 -0500 Subject: Towards cleaner nesting In-Reply-To: <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> Message-ID: <3e03bd62-a17f-ff94-031f-ef38aa1797b7@oracle.com> >> Of course, some of these don't make sense, such as "constructors in field initializers", so it's not quite as simple as that, but yes, we should seek to bust the _gratuitous_ restrictions. > Yeah, thinking a bit more, I guess I'd refine my list like so: > > Type declarations: > - top-level/static classes > - inner classes* > - interfaces > - records > - enums > - annotation types > > Method declarations: > - static methods > - instance/local methods* > - static initializers > - instance initializers/constructors* > > Variable declarations (leaf nodes, not containers): > - instance/local variables* > - static variables Well, variable declarations are not _quite_ leaf nodes: ??? int x = switch (foo) { ??????? case 1 -> { ??????????? class LocalToSwitch { } ??????????? yield new LocalToSwitch(); ?????? } ?? }; So variable initializers can have expressions in them, which can have statements in them ... which can have local classes.? In this way, static/instance variable initializers are a like static/instance class initializers. > And then "anything can be nested inside of anything" has the following exceptions: > > - interfaces can't directly contain instance initializers/constructors, instance/local variables, or inner classes (maybe) In order to have inner classes in an interface, we'd have to have a non-static modifier, since nested classes in interfaces are allowed now and are implicitly static. > - records can't directly contain instance/local variables > > - annotation types can't directly contain things prohibited in interfaces, or instance/local methods (maybe) ?- currently annos cannot have fields, default methods, or static methods -- this still seems a reasonable restriction. > - method declarations of any kind can't directly contain static initializers, instance initializers/constructors, static methods (maybe), or static variables (maybe) I think static local methods make perfect sense here, because the meaning of static is being broadened to cover the capture of both instance context and locals. > So, not quite "anything goes", but a much shorter and more refined list of prohibitions than we had previously. Yep. Your list doesn't treat constructors, either as "what can contain them" (pretty clear that's classes/records/enums only), or as "what can they contain" (could a constructor have, for example, a local method or a local class.) From brian.goetz at oracle.com Fri Jan 10 03:04:33 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 22:04:33 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> Message-ID: <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> I guess that means the stuff I said about "only having to look in two places" for null-handling was wrong; any static (declared) pattern could be nullable.? Which brings us back to the place that people didn't like the last time around -- you have to scrutinize the implementations of the patterns associated with all N cases to determine whether this switch will take nulls or not. On 1/9/2020 7:57 PM, John Rose wrote: > > We haven?t yet moved on to library-defined patterns (?static > patterns?). ?To gaze a bit into the crystal ball: ?I think it?s > likely that those will provide a way to express T? using > a library API instead of a language primitive. ?We should > defer building out that last 1% of null support either > indefinitely, or until there is a way for libraries to define > their own kinds of patterns. ?In the latter case the cost > of adding a nullable pattern is likely to be lower than > the alternatives on the table, since library changes are > always cheaper than language changes. -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Jan 10 03:17:13 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 10 Jan 2020 10:17:13 +0700 Subject: Component deprecation (was: [records] Record updates for Preview/2) In-Reply-To: <6255AEE6-C814-4225-B076-EFE6A4929F93@oracle.com> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> <6255AEE6-C814-4225-B076-EFE6A4929F93@oracle.com> Message-ID: I can imagine that the record component loses its meaning and now should be ignored. E.g. record Point(int x, int y, @Deprecated int z) {} // the budget was cut, so we are building 2D game now instead of 3D. In this case, to make it convenient for new call sites while keeping the old call sites compilable, it would be good to introduce non-canonical constructor: record Point(int x, int y, @Deprecated int z) { public Point(int x, int y) { this(x, y, 0); } } Probably we can mark a canonical constructor as deprecated if at least one record component is deprecated because using the canonical constructor you're essentially using the deprecated component. However, to me it looks much better to remove component at all in this case, and emulate it for old call sites: record Point(int x, int y) { @Deprecated public Point(int x, int y, int z) { this(x, y); } @Deprecated public int z() { return 0; } // or even throw } This should be source and binary compatible change. The only problem is the deserialization of old streams (provided the record is serializable). With best regards, Tagir Valeev. On Fri, Jan 10, 2020 at 6:42 AM Dan Smith wrote: > > > On Jan 9, 2020, at 10:51 AM, forax at univ-mlv.fr wrote: > > > > Currently you can not deprecate a record component unlike in Scala or in Kotlin. > > We can either allow @Deprecated or pretend that people will never make mistake, change their mind, specification will never change, etc > > Setting aside implementation details, and whether there's any change to be made here: I'm curious about what you intend a deprecated record component to *mean*. How do you deprecate a single parameter of a constructor? A concrete example could be useful?I can believe there might be something there, but I'm struggling to see it. > > A related StackOverflow discussion about deprecated parameters/locals: > https://stackoverflow.com/questions/14627313/what-does-deprecated-mean-on-method-parameters-and-local-variables > From brian.goetz at oracle.com Fri Jan 10 03:27:23 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 22:27:23 -0500 Subject: Component deprecation In-Reply-To: References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> <6255AEE6-C814-4225-B076-EFE6A4929F93@oracle.com> Message-ID: <7c416b6f-44b8-bc06-b040-d5bc62fffd7f@oracle.com> Note that to emulate the old protocol you would have to explicitly declare a 3-arg ctor _and deconstructor_. But there's no problem with that (once we have the brutal syntax discussion about deconstructor declaration (not now, Remi.)) I mostly agree that deprecation here is kind of silly, but since @Deprecated is "just" an annotation, I don't object to it being allowed. Serialization should be fine, as we go through a similar process for encoding/decoding fields as default serialization, so the extra field will be ignored. On 1/9/2020 10:17 PM, Tagir Valeev wrote: > I can imagine that the record component loses its meaning and now > should be ignored. > > E.g. record Point(int x, int y, @Deprecated int z) {} // the budget > was cut, so we are building 2D game now instead of 3D. > > In this case, to make it convenient for new call sites while keeping > the old call sites compilable, it would be good to introduce > non-canonical constructor: > > record Point(int x, int y, @Deprecated int z) { > public Point(int x, int y) { this(x, y, 0); } > } > > Probably we can mark a canonical constructor as deprecated if at least > one record component is deprecated because using the canonical > constructor you're essentially using the deprecated component. > > However, to me it looks much better to remove component at all in this > case, and emulate it for old call sites: > > record Point(int x, int y) { > @Deprecated public Point(int x, int y, int z) { this(x, y); } > @Deprecated public int z() { return 0; } // or even throw > } > > This should be source and binary compatible change. The only problem > is the deserialization of old streams (provided the record is > serializable). > > With best regards, > Tagir Valeev. > > On Fri, Jan 10, 2020 at 6:42 AM Dan Smith wrote: >>> On Jan 9, 2020, at 10:51 AM, forax at univ-mlv.fr wrote: >>> >>> Currently you can not deprecate a record component unlike in Scala or in Kotlin. >>> We can either allow @Deprecated or pretend that people will never make mistake, change their mind, specification will never change, etc >> Setting aside implementation details, and whether there's any change to be made here: I'm curious about what you intend a deprecated record component to *mean*. How do you deprecate a single parameter of a constructor? A concrete example could be useful?I can believe there might be something there, but I'm struggling to see it. >> >> A related StackOverflow discussion about deprecated parameters/locals: >> https://stackoverflow.com/questions/14627313/what-does-deprecated-mean-on-method-parameters-and-local-variables >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From guy.steele at oracle.com Fri Jan 10 03:39:39 2020 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 9 Jan 2020 22:39:39 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> Message-ID: <056C5007-1B8F-4019-BA74-EE312DF43D0D@oracle.com> Rats! I was hoping this approach would pan out. Oh, well. We can always use Box box; switch? (box) { case Box(Chocolate c): case Box(Frog f): case Box(var o): } :-) > On Jan 9, 2020, at 10:04 PM, Brian Goetz wrote: > > I guess that means the stuff I said about "only having to look in two places" for null-handling was wrong; any static (declared) pattern could be nullable. Which brings us back to the place that people didn't like the last time around -- you have to scrutinize the implementations of the patterns associated with all N cases to determine whether this switch will take nulls or not. > > On 1/9/2020 7:57 PM, John Rose wrote: >> >> We haven?t yet moved on to library-defined patterns (?static >> patterns?). To gaze a bit into the crystal ball: I think it?s >> likely that those will provide a way to express T? using >> a library API instead of a language primitive. We should >> defer building out that last 1% of null support either >> indefinitely, or until there is a way for libraries to define >> their own kinds of patterns. In the latter case the cost >> of adding a nullable pattern is likely to be lower than >> the alternatives on the table, since library changes are >> always cheaper than language changes. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Jan 10 03:47:25 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 10 Jan 2020 10:47:25 +0700 Subject: [record] Marker annotation for overridden accessors Message-ID: Hello! Java has a good habit of explicitly marking specific code elements with annotations when they convey a special meaning to avoid accidental loss of this meaning. E.g.: @Override: method overrides a superclass method: it's an error if it doesn't @FunctionalInterface: an interface is a functional interface: it's an error if it doesn't @Serial: a method/field is a part of serialization infrastructure: not sure whether it's completely implemented, but it would be a good idea to issue error/warning if a wrong method is annotated. Now we have new "special" methods: record accessors. They cannot be annotated with @Override but they technically override the auto-generated implementation. Probably we should introduce a new annotation that designates the intention to override the accessor? E.g. record Foo(List list) { @RecordAccessor public List list() { return Collections.unmodifiableList(this.list); } } So if we rename the method without renaming the record component, we will have a compilation error now. What do you think? I also thought about an annotation to designate the canonical constructor and concluded that it's useless because it's impossible to mistakenly make a constructor non-canonical. E.g.: record Interval(int from, int to) { public Interval(int from, int to) { if(from < to) { this.from = from; this.to = to; } else { this.to = from; this.from = to; } } } We may want to add a new component, e.g. `boolean closed` and forget to update the canonical constructor. However, this immediately creates a compilation error, because new implicit canonical constructor will be generated, and now existing explicit constructor must delegate. Essentially, in a working program, the absence of delegation in the constructor body already marks the constructor as canonical. With best regards, Tagir Valeev. From john.r.rose at oracle.com Fri Jan 10 03:53:25 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 19:53:25 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> Message-ID: <09D15DA1-225C-4619-944B-97432E2B3140@oracle.com> On Jan 9, 2020, at 7:04 PM, Brian Goetz wrote: > > I guess that means the stuff I said about "only having to look in two places" for null-handling was wrong; any static (declared) pattern could be nullable. Which brings us back to the place that people didn't like the last time around -- you have to scrutinize the implementations of the patterns associated with all N cases to determine whether this switch will take nulls or not. Yeah; I wondered if you?d say that. This is a problem with methods of any kind, isn?t it? Programmers must always wonder what?s inside any given method call. We have javadoc and @NotNull annotations to help, but it?s always going to be difficult. That said, library patterns will surely be composable from (or along side) more primitive patterns, and we (perhaps) could also require that the various pieces and parts of a null-friendly library pattern would visibly ?let the null in? to the library code, and failing that the null would be rejected. Straw man: Declare that all static patterns are non-total, and exclude null from them on the same principle as today?s non-total patterns. This requires a library pattern to be treated as always non-total. Straw man: Allow static patterns to explicitly mention the ?receiver? of the match, as an optional precursor pattern. Key null-transmission on whether that precursor pattern is total or not, under today?s rules. By precursor pattern I mean the occurrence of the pattern ?String? in the following straw man syntaxes: // static __Pattern myLibPattern(__MatchTarget Object target, int modes); switch (o) { case myLibPattern(0x42): ? case myLibPattern(__MatchTarget String x, 0x42): ? case String x & myLibPattern(0x42): ? case (String x).myLibPattern(0x42): ? // this looks *really* null hostile! ?etc? } This depends sensitively on the concrete design of library patterns, but I?m assuming the usual connection points of (a) the value being matched, (b) the outputs to the match (if any), and (c) additional parameters (?modes?). My overall point here is that there are plausible options to keeping the null under control, even visibly so. And we can stack the deck so that letting in the null is always accompanied by some telltale signal, not just a tacit default. I think, when we cross this bridge, we can appeal to some combination of documentation, good style, and explicit structure in the case pattern. Here?s another example to suggest some various degrees of freedom, and some pitfalls: static __Pattern ofClass( __MatchTarget Object x, Class c, boolean nullOK, __MatchResult T x2) { if (c.isInstance(x)) __Match(x2 = c.cast(x)); else if (nullOK) __Match(x2 = null); else __Fail; } // (not the real syntax) boolean z = ?; Object o = ?; switch (o) { case Objects.ofClass(String.class, z, var s): return 1; //worst case case Objects.ofClass(Number.class, true, var n): return 2; //RTFJD case Objects.ofClass(List.class, false, var x): return 3; //ditto default: return 0; //NPE here??? } > On 1/9/2020 7:57 PM, John Rose wrote: >> >> We haven?t yet moved on to library-defined patterns (?static >> patterns?). To gaze a bit into the crystal ball: I think it?s >> likely that those will provide a way to express T? using >> a library API instead of a language primitive. We should >> defer building out that last 1% of null support either >> indefinitely, or until there is a way for libraries to define >> their own kinds of patterns. In the latter case the cost >> of adding a nullable pattern is likely to be lower than >> the alternatives on the table, since library changes are >> always cheaper than language changes. > From brian.goetz at oracle.com Fri Jan 10 03:54:50 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 9 Jan 2020 22:54:50 -0500 Subject: [record] Marker annotation for overridden accessors In-Reply-To: References: Message-ID: <86090ef9-c7ea-7c16-366d-82bb005c1ef4@oracle.com> I would not be in favor of creating a new annotation `@LikeAnOverrideButNotReally`, but I could imagine interpreting `@Override` to mean "must either override a member in a supertype, or implement a mandated member."? (This is true not only of the accessors, but also of the canonical ctor and dtor.) On 1/9/2020 10:47 PM, Tagir Valeev wrote: > Hello! > > Java has a good habit of explicitly marking specific code elements > with annotations when they convey a special meaning to avoid > accidental loss of this meaning. E.g.: > > @Override: method overrides a superclass method: it's an error if it doesn't > @FunctionalInterface: an interface is a functional interface: it's an > error if it doesn't > @Serial: a method/field is a part of serialization infrastructure: not > sure whether it's completely implemented, but it would be a good idea > to issue error/warning if a wrong method is annotated. > > Now we have new "special" methods: record accessors. They cannot be > annotated with @Override but they technically override the > auto-generated implementation. Probably we should introduce a new > annotation that designates the intention to override the accessor? > E.g. > > record Foo(List list) { > @RecordAccessor > public List list() { > return Collections.unmodifiableList(this.list); > } > } > > So if we rename the method without renaming the record component, we > will have a compilation error now. What do you think? > > I also thought about an annotation to designate the canonical > constructor and concluded that it's useless because it's impossible to > mistakenly make a constructor non-canonical. E.g.: > > record Interval(int from, int to) { > public Interval(int from, int to) { > if(from < to) { this.from = from; this.to = to; } > else { this.to = from; this.from = to; } > } > } > > We may want to add a new component, e.g. `boolean closed` and forget > to update the canonical constructor. However, this immediately creates > a compilation error, because new implicit canonical constructor will > be generated, and now existing explicit constructor must delegate. > Essentially, in a working program, the absence of delegation in the > constructor body already marks the constructor as canonical. > > With best regards, > Tagir Valeev. -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Fri Jan 10 04:45:39 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 20:45:39 -0800 Subject: [records] Record updates for Preview/2 In-Reply-To: References: Message-ID: <94B34087-7A5F-4526-8BF5-60FFD1AF644C@oracle.com> On Jan 8, 2020, at 3:55 PM, Brian Goetz wrote: > > We're gathering a list of what non-bugfix things we want to do for the second preview of records. So far, on my list, I have: > > 1. Revisiting the j.l.Record supertype. We want to support inline records when we have inline types. Until now, we've been working on the assumption in Valhalla that inline classes can only extend interfaces, not abstract classes, so it was suggested that Record should be an interface. However, that topic in Valhalla is under reconsideration, and I want to wait until that discussion plays out before making any changes here. > > It has also been pointed out that the name Record is somewhat clash-y. I'm not really willing to pick a lousy name just to reduce the chance of clashes, but I might be OK with a name like RecordClass. (Bikeshed alert: if you want to discuss any of these topics, please start a new thread; this one is about curating a to-do list.) > > 2. Accessibility of mandated members. Remi noted that the requirement that the mandated record members be public, even for non-public classes, was weird. But, at the time, the spec was in a state that trying to revisit this was impractical -- Gavin has now left the spec in a much cleaner place, and so it is reasonable to reopen this discussion. The leading alternate candidate is to propagate the accessibility of the record to its mandated members (public for public, etc), but still require the author to say what they mean. +1 on going another round on accessibility. I specifically want to make sure we are doing the best thing on access defaults (what happens when you don?t mention *any* access mode). I suppose that?s a separate item, related to this one. Even though we have a decision of record on this, I?m calling it out here and now because we are stuck with the final decision we make this time around. (Maybe like a mandatory appeal for a capital case.) > 3. Nesting considerations. In 14 we will fix the issues surrounding local records, but we still ban nested records in non-static classes. We should fix this -- by dropping the restriction on static members in inner classes -- and then bring records, enums, and interfaces to parity (allowing local and nested flavors of all, all implicitly static.) Agreed. It?s time to let static types (and maybe other statics) nest more freely. (The reasons for the current restrictions are IMO obsolete, because we will never take the path of making inner classes into true dependent types. Back in the day we thought we would leave that door open a crack. If perchance we do dependent types in the future, a la Beta and Scala, we?ll surely declare them explicitly.) > 4. Abstract records. Several people have asked "what about abstract records"; while these are theoretically possible, there are some complications that I think are best left for treating these as a later addition if needed. But, for the record, here are some thoughts from the last time I looked into this. > > Given that records are nominal tuples, the notion of "width subtyping" comes immediately to mind. So, let's go with that for a moment: you could say > > abstract record A(int a, int b) { } > and > record B(int a, int b, int c) extends A { } > > and there is a natural width subtyping relationship. We don't have problems with the equality contract because the abstract class leaves equals() abstract. > > But, this is a story that is likely to not be entirely satisfactory. Do we require that the state of A form a prefix for the state of B? This may not be the API people want. Do we require that super(a,b) be passed through unchanged? The constraints on the subclass in this model get, constraining. > > What if there were a more flexible relationship: > > record B(int a, int b, int c) extends A(a, b) { } > > Now, there's more flexibility, at the cost of a new "extends" construct. And what if you want to fix some field of A as a constant: > > record iload2_bytecode() extends bytecode(0x1c) { } > > These all seems like reasonable things to want when you get into abstract records ... but they all start to push on the "records imperative". So for now, we're doing nothing, until we have a better story for what we actually want to do. Personally, I don?t have much appetite for abstract records, any more than I do for the notion of tuple inheritance. (int x, int y) is not a subtype of (int x). Remi said it well: Inheritance is not the right mechanism to express such things. *But.* I *do not* want this decision to accidentally drive the translation strategy. Just because we don?t have an abstract class to plant ?toString? in doesn?t mean we are dispensed from defining a modular way to manage ?toString? on records. We might start with a hardwired public API for this, but I think we also want to keep an eye on Brian?s dreams for wiring up implementations by delegation. Where I think this goes is towards a record implementation class with a public API which (a) works well today and (b) is likely to be the starting point of a more pluggable story for behaviorally polymorphic APIs like the toString of record. (And the equals and hashCode.) The libraries, not the language, should define these methods, and the shape of the definition should be future-friendly. > 5. Deconstruction patterns. Yes, records should be deconstructible with deconstruction patterns. > > Anything else, that we've already discussed that I left out? 6. Default access modes in records. (See above. ) Choices: Package scope like classes, public like interfaces, something new for records. 7. Translation strategy. (Is it polished and future-friendly?) In the review it?s done by means of jlr.ObjectMethods::bootstrap, which seems a little non-specific to records. Doing something with a simple-enough indy (and no other javac-generated code!) is probably future-friendly. When and if we get ?mindy? (indy-flavored method definitions) the indy instruction can be quietly retired in favor of a mindy-bootstrap, with no semantic change. Beyond that, the happy point I want to get to in the future is a sort of inherited mindy, where javac doesn?t mention ?toString? at all in any give record classfile, and instead there?s some wiring somewhere that says, ?records conform to Object via this mindy provider?. That verges on an abstract class or even an interface; do we need or want to add something like that as a mandated feature of all records? Maybe not. But I do think we want to get a foot into the door somehow with records, today, that we parley into a channel for new features tomorrow. 8. Transactional methods (aka. reconstructors): Consider generalizing existing patterns that support multi-component data processing. This can be left for the future, but it?s worth a look now in order to make sure today?s similar forms (which are canonical constructors and deconstruction methods) don?t accidentally tie our hands. What?s a ?transactional? method (or expression or lambda)? OK, I just made up the term, and I?ll have a different one tomorrow, but the concept has been on the table for a long time. In essence there?s a set of names (record component names) which are put in scope at the top of a block, may be sequentially modified by the block, and are committed at the bottom. I?ll punt the rest of the discussion to a separate email. 9. If we don?t decide hashCode explicitly, I think we?ll back ourselves into a bad default decision. Should the hashing logic for r.hashCode() be specified in a portable manner? I say, ?no?, since there?s no good portable solution. Objects.hash is portable, but other than that is a poor algorithm. I?ll fork a separate thread for this. Right now we don?t *specify* that we combine hashes using the base-31 polynomial, but we *do* use that stupid old thing. This is plausibly conservative but it will get it into a worse spot than necessary. We can buy ourselves some time by putting weasel words into Record::hashCode (or its successor) and ObjectMethods::bootstrap. We can promise that the hashCode result depends (typically) on all components of the record, and on nothing else. We should also explicitly state that the method for combining the bits is subject to change, very much like we specify that the iteration order for Set.of and Map.of is subject to change, like this: http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/Set.java#l83 Finally, for luck, we should fold a dash of salt into the hash function to keep users on their toes, like this: http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/ImmutableCollections.java#l619 If we aren?t cautious about this, users will eventually discover that we are using the Objects.hash algorithm, we will document it, and then we?ll be locked out from an easy improvement to records. I don?t think we should even promise that the record hashCode is mixed from the hashCodes of the components. That?s TMI, and it boxes us in. (I want a vectorized hashCode someday for records and inlines.) All we need to do is promise that a record type?s hashCode fulfills the Object contract, especially relative to the record?s equals method. No other promises, other than that the hashCode is subject to change. And then rub some salt on it to change it. Those are my remaining concerns. ? John From john.r.rose at oracle.com Fri Jan 10 04:45:42 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 20:45:42 -0800 Subject: [records] updates for Preview 9. hashCode Message-ID: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> If we don?t decide hashCode explicitly, I think we?ll back ourselves into a bad default decision. Should the hashing logic for r.hashCode() be specified in a portable manner? I say, ?no?, since there?s no good portable solution. Objects.hash is portable, but other than that is a poor algorithm. Right now we don?t *specify* that we combine hashes using the base-31 polynomial, but we *do* use that stupid old thing. This is plausibly conservative but it will get it into a worse spot than necessary. We can buy ourselves some time by putting weasel words into Record::hashCode (or its successor) and ObjectMethods::bootstrap. We can promise that the hashCode result depends (typically) on all components of the record, and on nothing else. We should also explicitly state that the method for combining the bits is subject to change, very much like we specify that the iteration order for Set.of and Map.of is subject to change, like this: http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/Set.java#l83 Finally, for luck, we should add a pinch of salt to the hash function to keep users on their toes, like this: http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/ImmutableCollections.java#l619 (It?s an implementation detail, but for now this combiner would be enough to buy us a better future: `(h,x) -> ((h*31)+x)^SALT`) If we aren?t cautious about this, I?m afraid that users will eventually discover that we are using the Objects.hash algorithm, we will be pressured to document it, and then we?ll be locked out from an easy improvement to records. It?s easier to introduce the salt before that happens. I don?t think we should even promise that the record hashCode is mixed from the hashCodes of the components. That?s TMI, and it boxes us in without telling the user anything useful. All we need to do is promise that a record type?s hashCode fulfills the Object contract, especially relative to the record?s equals method. No other promises, plus a warning that result is subject to change. And then rub some salt on it to keep it clean, as with Set.of and Map.of. ? John From john.r.rose at oracle.com Fri Jan 10 04:54:30 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 20:54:30 -0800 Subject: [records] updates for Preview, 8. transactional constructs Message-ID: <63310806-D769-4564-AF62-174AD6BA1DD1@oracle.com> 8. Transactional methods (aka. reconstructors): Consider generalizing existing patterns that support multi-component data processing. This can be left for the future, but it?s worth a look now in order to make sure today?s similar forms (which are canonical constructors and deconstruction methods) don?t accidentally tie our hands. Here?s what I mean by transactional constructors, and specifically by ?reconstructors?. These concepts interact strongly with both constructors and reconstructors (of records and inlines), and I want to air them out so we have a chance to grow what we have now into something even better. FTR, I think we are in a good place at the moment. So there aren?t any changes to propose for the current Preview. A. The components of a record appear, in some places, to work just like variable names. (In other places they look like fields or access functions.) B. In a canonical constructor, the names are set to parameter values and on exit are committed to (final) fields. Until the end of the constructor, sequential code can get and set them. C. In a deconstruction, the names are set to field values, are passed (by means undefined) to the caller, and on return are committed to pattern variables. Until the end of the deconstructor, sequential code can get and set them. (Yes? No? Defensive copying is a potential use case for setting. Testing of special query conditions is a potential use case for getting.) D. Observation: In both cases, all record components are in scope at the top of a block, can be processed by sequential code inside the block, and are committed to some result at the bottom of the block. Such a block as multiple named inputs and outputs; the inputs and outputs are the same set of names. (In some cases the outputs might be a proper subset of the inputs.) Let?s call such a block, where the components of a type are in scope *and* may be updated for use at the *end* of the scope, a transactional block. E. For Java 1.0 classes with no blank final fields, *every* method body is transactional (on the non-static non-final fields), in the above sense. At the top of the block, the names are ready for use by sequential code. After updates, the names are committed to the instance storage. (Yes, they are *already* committed; that?s not so ?transactional?, but often programmers don?t care about this detail.) E. For Java classes with final instance fields (and thus with inline classes and records), every method is halfway transactional, in the sense that all field names are readable. They are not writable, and hence are not meaningfully committed at the bottom of the method body. F. Likewise, constructors in such classes are all halfway transactional, because the relevant components (the blank final fields) are committed at (by) the end of the block. The DA/DU checking rules ensure that they are all committed. G. *Reconstructors* (possible future feature for inline types and/or records) are methods whose bodies are transactional blocks. With respect to inputs, they behave like methods as described in E above. With respect to outputs, they behave like constructors (hence the name) as described in F above. Reconstructors are not very appealing for classic identity objects, because a new identity would have to be created for each method call. But for inline objects, especially quasi-stateful ones like cursors, they are the right notation for the job. ``` inline class Cursor(double[] a, int i) { Cursor next() { return new Cursor(a, i+1); } __Reconstructor next() { i++; } } ``` H. By a process of analogy, this concept might be transferrable to lambdas, expressions, and maybe more. A transactional lambda might be one which can be called from within a transactional block, and would be free to update any or all of an agreed set of names, wiring the results back to the caller. Like `myRow.withTransaction(__TL{sal+=1000;})`. A transactional expression might be an ad hoc bit of external logic presented to update an object, as `cur = cur.__TE(i++)`. I?m *not* proposing these features now, because we don?t know quite enough yet to define them, but I want to keep the door open for such things in our present decisions. Which I think we are, and will continue if we pay some attention. From john.r.rose at oracle.com Fri Jan 10 05:09:36 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 21:09:36 -0800 Subject: [records] translation strategy Message-ID: 7. Translation strategy. (Is it polished and future-friendly?) In the review it?s done by means of jlr.ObjectMethods::bootstrap, which seems a little non-specific to records. Doing something with a simple-enough indy (and no other javac-generated code!) is probably future-friendly. When and if we get ?mindy? (indy-flavored method definitions) the indy instruction can be quietly retired in favor of a mindy-bootstrap, with no semantic change. Beyond that, the happy point I want to get to in the future is a sort of inherited mindy, where javac doesn?t mention ?toString? at all in any give record classfile, and instead there?s some wiring somewhere that says, ?records conform to Object via this mindy provider?. That verges on an abstract class or even an interface; do we need or want to add something like that as a mandated feature of all records? Maybe not. But I do think we want to get a foot into the door somehow with records, today, that we parley into a channel for new features tomorrow. Actually, I?ll ask this another way: Are we comfortable with saying that records may in add new features in the future, without requiring opt-in? Perhaps even without recompilation? I think, ?yes?, because the alternative is that the features records have as of the first non-preview release is all there will ever be. In any case, that calls the question of how we prepare for this. Choices: A. Specify a super type now, and add the new API connection points to the super type. It could be an interface or an abstract class. B. Have the JVM inject a super-interface later, when we figure out we want new API points. C. Use some ad hoc ?code spinning? technique, without appealing to inheritance. When there?s an indy to spin from, great, but otherwise do it at arms? length. A. is cleanest but maybe we can?t decide how to deploy inheritance here. B. has one very bad bit of special pleading followed by inheritance later. C. is all special pleading. Suppose we add a new kind of serialization to records later on, and we don?t want to require an opt-in or even a recompile. It looks like this, then: A. Add necessary API points to the record super, and do code spinning behind the scenes as an optimization. New records can override the new API points to get fuller control over the new feature. A2. Optionally have compilers insert indy points for easier memoization of spun code. But don?t require them, for the sake of old code. B. Same as A if the JVM has started injecting the record super. Otherwise same as C. C. For the necessary new customized API points, do the code spinning and caching in a set of static methods and a static table (or the ClassValue API). C2. Use reflection to look for ?opt in? quasi-overrides in each record, to give back some control to the user. When I say ?code spinning? I mean any kind of ad hoc compiler-like code generation, to bytecodes, to method handles, or to something else. That ?something else? might eventually be template methods; seems like a possible story for us; it works in C++. From john.r.rose at oracle.com Fri Jan 10 06:21:25 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 22:21:25 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <056C5007-1B8F-4019-BA74-EE312DF43D0D@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> <056C5007-1B8F-4019-BA74-EE312DF43D0D@oracle.com> Message-ID: <1962DCD0-9E62-478B-A9C7-90FEFEAF4C2A@oracle.com> On Jan 9, 2020, at 7:39 PM, Guy Steele wrote: > > Rats! I was hoping this approach would pan out. Don?t give up, just because library functions can be inscrutable! -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Fri Jan 10 06:36:11 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 22:36:11 -0800 Subject: [records] translation strategy (factory methods) In-Reply-To: References: Message-ID: Another bit of translation strategy: What does ?new R(a,b,c)? compile to, at the bytecode level? At some point we will cut over to factory methods, for at least some types (inlines). Should be pre-empt this trend for records, and mandate that records are *always* created by factory methods? The benefit is that records can be evolved to inline types without recompiling their *clients*. The risk is that (if we don?t take this option) that we will have records which are constrained to be identity] types *until all clients are recompiled*. (No I don?t believe that you can translate ?new;dup;invokespecial? into ?invokestatic?. Sorry, that's a pipe dream.) This seems sad, given that records are partially inspired by value types. (From my 2012 blog on value types: ?A value type is immutable? The equals and hashCode operations are strictly component-wise.? Kind of like a record. Meanwhile, inlines in Valhalla give component-wise meaning to acmp not equals and identityHashCode not hashCode.) I see three options regarding factories (apart from the above pipe dream): 0. Do nothing. Records are just abbreviated classes. Whatever works or doesn?t for evolving general identity classes to inline classes works or doesn?t for records. No special benefit, in this vein, to declaring something a record. 1. Define a factory, on top of today?s JVM. Records use a hidden API, desugaring ?new R(a,b)? to a static ?R.$make$(a,b)?. 2. Define a factory, anticipating Valhalla, using a JVM-defined entry point. The expression ?new R(a,b)? compiles to ?invokestatic R.(int,int)R?, preceded by a push of a and b. This requires a (relatively shallow) change to the JVM to double down on the name ?? as the factory behind a constructor. The advantage of 2 is that, if we correctly predict that ?? is the claimed factory method for value types, then records can be evolved to inlines out of the box. The advantage of 1 is that we don?t need to make that prediction. There?s also this: 3. Do 1. but when 2. becomes an option create an auto-bridge from ?$make$? to ??. New code doesn?t need the bridge because it compiles to call ?? out of the box. Here?s a gut check for this group: Are we confident enough with Valhalla that we can settled on ?invokestatic ? as the new dance for creating an instance that may or may not be an inline? ? John From john.r.rose at oracle.com Fri Jan 10 06:40:01 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 22:40:01 -0800 Subject: [records] updates for Preview 6. Default access modes Message-ID: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> 6. Default access modes in records. (See above. ) I specifically want to make sure we are doing the best thing on access defaults (what happens when you don?t mention *any* access mode). I suppose that?s a separate item, related to this one. Even though we have a decision of record on this, I?m calling it out here and now because we are stuck with the final decision we make this time around. (Maybe like a mandatory appeal for a capital case.) Choices: A. Package scope like classes, B. public like interfaces, C,D,? something new for records. A. Records are concise classes which represent a state vector of components, plus optional additional behavior. If a record member is declared with no access mode, it is given the package access mode. Just like classes. The word ?public? must be reaffirmed for every API in the record; just like classes, the presumption is that API points should be private by default, and only made public via explicit mark-up. B. Records are state vectors of components, plus optional additional behavior. They are not general classes, but rather constrained types, much like interfaces are behavior vectors of abstract API points, plus optional behavior. If a record member is declared with no access mode, it is given the public access mode. Just like an interface, a record is a restricted type, whose contracts with the outside world are mainly public, and which has a limited capacity for non-public contracts. C. Records are state vectors of components, plus optional additional behavior. The parts of their API which are intended to be mostly public are public by default, at least if the record type as a whole is public. The precedent for this variability is the implicit empty nullary constructor supplied to every class, whose access mode is copied from the class itself. The parts of a record API that are expected to be public are the factory (canonical constructor) and the accessors (component projection methods). So those guys are public by default (if the record is public). (If the record isn?t public those other guys are the same.) At present I prefer ?B?, because I now think that, regarding the access expectiations of API points, records are *far more similar to interfaces* than they are to random run of the mill classes. We started with random classes, but after applying enough constraints we now have API elements which are closely analogous to interfaces: Bundles of state, bundles of operations. And, for wide use; i.e. few or no secrets. In both cases, ?public? is the sane default. An advantage of ?B? is there?s nothing new to teach. ?You know how interfaces have their own default access? Well, records use the same rules.? ? John From john.r.rose at oracle.com Fri Jan 10 06:51:52 2020 From: john.r.rose at oracle.com (John Rose) Date: Thu, 9 Jan 2020 22:51:52 -0800 Subject: [records] updates for Preview 6. Default access modes In-Reply-To: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> Message-ID: <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> P.S. The alert reader has perhaps already said, ?but wait, if you make records like interfaces, then records cannot define non-public APIs!? Very good, alert reader, that?s just like interfaces. And we can amend *both* by adding some way to overcome the default ?public? with a new modifier that means ?I know you default to public, and I know the normal syntax for package access is to say nothing and obtain a different default, and I want that package access, please.? Potential spellings for that modifier are ?package? and ?non-public?. In interfaces, haven?t you ever wanted to define a non-public nested class which implements the interface or carries some other interesting implementation data? The JVM has no objection to you doing so, but the language does. It would be helpful to allow an interface to define a non-public nested class, for the sake of modularity. The ?package? or ?non-public? modifier would support this. Even for garden variety classes there?s a use case for such a modifier. Haven?t you ever wanted to mark an API point, ?don?t make this public, for heaven?s sake?? I use the comment ?/*non-public*/? which drives maintainers crazy. Saying ?non-public? or ?package? would be a better way to document the intention. From amaembo at gmail.com Fri Jan 10 10:04:42 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 10 Jan 2020 17:04:42 +0700 Subject: [records] updates for Preview 6. Default access modes In-Reply-To: <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> Message-ID: I don't like B. The problem with B is that having a public default you may mistakenly expose an implementation detail, and it could be hard to fix this when clients start to use it. With interface methods this is less important, because if you have an implementation inside interface, you should explicitly mark it with either 'default' (meaning that it's still part of an interface) or 'private' (meaning that it's an implementation detail). But specifying an interface method with a body without a modifier at all is an error, so you will have to think. Records are not of this kind. They are not abstract, every method has an implementation, and it's perfectly valid to add a method without specifying any modifier. You will have all the tests passing, etc., but you're still doing a mistake, which is expensive to fix. Of these options, I like C the most, though I would modify it: D. The access modifier for explicit overloads to synthetic members (canonical constructor & accessors) must be the same or more permissive than an access modifier for the record itself. The absence of access modifier means 'package-private', as usual for classes. This would require 'public' for public records, and it's nice because you will see that it's really a part of the API. On the other hand, this would reduce noise for inner/local records where you can omit the access modifier. And still, you can specify public on local records, so you can easily reduce the record visibility without the need to modify all the members. Adding an explicit package/non-public modifier is a good idea per se, but it's orthogonal to the records case. We can implement it (allowing package-private members in interfaces), yet stick with my D proposal. With best regards, Tagir Valeev. On Fri, Jan 10, 2020 at 1:52 PM John Rose wrote: > > P.S. The alert reader has perhaps already said, ?but wait, if you > make records like interfaces, then records cannot define non-public > APIs!? Very good, alert reader, that?s just like interfaces. And we > can amend *both* by adding some way to overcome the default > ?public? with a new modifier that means ?I know you default to > public, and I know the normal syntax for package access is to > say nothing and obtain a different default, and I want that package > access, please.? Potential spellings for that modifier are ?package? > and ?non-public?. > > In interfaces, haven?t you ever wanted to define a non-public nested > class which implements the interface or carries some other interesting > implementation data? The JVM has no objection to you doing so, but > the language does. It would be helpful to allow an interface to define > a non-public nested class, for the sake of modularity. The ?package? > or ?non-public? modifier would support this. > > Even for garden variety classes there?s a use case for such a modifier. > Haven?t you ever wanted to mark an API point, ?don?t make this public, > for heaven?s sake?? I use the comment ?/*non-public*/? which drives > maintainers crazy. Saying ?non-public? or ?package? would be a better > way to document the intention. > From forax at univ-mlv.fr Fri Jan 10 11:14:00 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 10 Jan 2020 12:14:00 +0100 (CET) Subject: equals/hashCode/toString implementation In-Reply-To: References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> Message-ID: <558986163.785925.1578654840364.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 9 Janvier 2020 16:07:50 > Objet: Re: [records] Record updates for Preview/2 >> - @Deprecated on record components > Please outline what you think we want on a separate thread? >> - change the bootstrap method protocol (a little bit) > Let's start a thread on this too. >> - equals/hashCode implementation are currently slooooow > Can you qualify/quantify "slow"? sure ! toString is very very very slow, equals can be a little bit faster, hashCode has no issue. Using this benchmark [1]: Benchmark Mode Cnt Score Error Units ObjectMethodsBenchMark.equals_class avgt 5 67.953 ? 0.145 ns/op ObjectMethodsBenchMark.equals_record avgt 5 70.654 ? 0.154 ns/op ObjectMethodsBenchMark.hashCode_class avgt 5 2.741 ? 0.002 ns/op ObjectMethodsBenchMark.hashCode_record avgt 5 2.288 ? 0.002 ns/op ObjectMethodsBenchMark.toString_class avgt 5 20.381 ? 0.066 ns/op ObjectMethodsBenchMark.toString_record avgt 5 713.522 ? 14.290 ns/op - toString currently uses String.format() so it's dog slow. Wiring the bootstrap method to use the StringConcatFactory will fix the issue. - equals doesn't test primitive first so it's just a little bit slower that if I write it by hand So it's not slow compared to any code generated by an IDE or things like Lombock but it can be faster. Sorting the record components to have the 32 bits primitive types tested first (so create the corresponding guardWithTest last) is the usual trick. R?mi [1] [ https://github.com/forax/amber-record/blob/master/src/test/java/fr.umlv.record/fr/umlv/record/ObjectMethodsBenchMark.java | https://github.com/forax/amber-record/blob/master/src/test/java/fr.umlv.record/fr/umlv/record/ObjectMethodsBenchMark.java ] ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From chris.hegarty at oracle.com Fri Jan 10 11:57:57 2020 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Fri, 10 Jan 2020 11:57:57 +0000 Subject: Component deprecation In-Reply-To: <7c416b6f-44b8-bc06-b040-d5bc62fffd7f@oracle.com> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> <6255AEE6-C814-4225-B076-EFE6A4929F93@oracle.com> <7c416b6f-44b8-bc06-b040-d5bc62fffd7f@oracle.com> Message-ID: <2015b531-7200-103f-f0b4-63f113c524c4@oracle.com> On 10/01/2020 03:27, Brian Goetz wrote: >... > > I mostly agree that deprecation here is kind of silly, but since > @Deprecated is "just" an annotation, I don't object to it being allowed. > > Serialization should be fine, as we go through a similar process for > encoding/decoding fields as default serialization, so the extra field > will be ignored. That is correct. Record Serialization will deserialize whatever stream fields that are described by the stream class descriptor, but only pass the canonical constructor the stream values that match. Any unmatched stream field values are effectively discarded ( just like with ordinary object deserialization ). -Chris. From forax at univ-mlv.fr Fri Jan 10 13:05:34 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 10 Jan 2020 14:05:34 +0100 (CET) Subject: [records] updates for Preview 6. Default access modes In-Reply-To: References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> Message-ID: <1204388143.841750.1578661534175.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Tagir Valeev" > Cc: "amber-spec-experts" > Envoy?: Vendredi 10 Janvier 2020 11:04:42 > Objet: Re: [records] updates for Preview 6. Default access modes > I don't like B. The problem with B is that having a public default you > may mistakenly expose an implementation detail, and it could be hard > to fix this when clients start to use it. With interface methods this > is less important, because if you have an implementation inside > interface, you should explicitly mark it with either 'default' > (meaning that it's still part of an interface) or 'private' (meaning > that it's an implementation detail). But specifying an interface > method with a body without a modifier at all is an error, so you will > have to think. Records are not of this kind. They are not abstract, > every method has an implementation, and it's perfectly valid to add a > method without specifying any modifier. You will have all the tests > passing, etc., but you're still doing a mistake, which is expensive to > fix. > > Of these options, I like C the most, though I would modify it: > D. The access modifier for explicit overloads to synthetic members > (canonical constructor & accessors) must be the same or more > permissive than an access modifier for the record itself. The absence > of access modifier means 'package-private', as usual for classes. This > would require 'public' for public records, and it's nice because you > will see that it's really a part of the API. On the other hand, this > would reduce noise for inner/local records where you can omit the > access modifier. And still, you can specify public on local records, > so you can easily reduce the record visibility without the need to > modify all the members. yes, i vote for D too. I really dislike the noise, i.e. to have to write public in front of an accessor or a compact constructor when the whole record is declared private just above. > > Adding an explicit package/non-public modifier is a good idea per se, > but it's orthogonal to the records case. We can implement it (allowing > package-private members in interfaces), yet stick with my D proposal. > > With best regards, > Tagir Valeev. R?mi > > On Fri, Jan 10, 2020 at 1:52 PM John Rose wrote: >> >> P.S. The alert reader has perhaps already said, ?but wait, if you >> make records like interfaces, then records cannot define non-public >> APIs!? Very good, alert reader, that?s just like interfaces. And we >> can amend *both* by adding some way to overcome the default >> ?public? with a new modifier that means ?I know you default to >> public, and I know the normal syntax for package access is to >> say nothing and obtain a different default, and I want that package >> access, please.? Potential spellings for that modifier are ?package? >> and ?non-public?. >> >> In interfaces, haven?t you ever wanted to define a non-public nested >> class which implements the interface or carries some other interesting >> implementation data? The JVM has no objection to you doing so, but >> the language does. It would be helpful to allow an interface to define >> a non-public nested class, for the sake of modularity. The ?package? >> or ?non-public? modifier would support this. >> >> Even for garden variety classes there?s a use case for such a modifier. >> Haven?t you ever wanted to mark an API point, ?don?t make this public, >> for heaven?s sake?? I use the comment ?/*non-public*/? which drives >> maintainers crazy. Saying ?non-public? or ?package? would be a better >> way to document the intention. From gavin.bierman at oracle.com Fri Jan 10 13:31:30 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 10 Jan 2020 13:31:30 +0000 Subject: [records] New draft spec for JEP 359 Message-ID: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> Happy new year everyone! An updated version of the records spec is available at: http://cr.openjdk.java.net/~gbierman/jep359/latest (which currently points to http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) This addresses: * Removes `final` modifier on record components * Adding missing annotation target PARAMETER * Corrects discussion of @SafeVarargs annotation * Forbids capture of local variables in a local record * Clarifies implicit formal parameter list in a compact constructor Gavin From gavin.bierman at oracle.com Fri Jan 10 13:32:26 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 10 Jan 2020 13:32:26 +0000 Subject: Record component annotations with target=PARAMETER In-Reply-To: <09EDC6BB-BA75-429F-984E-9950E4F0AC67@oracle.com> References: <1f3e5c12-6f82-6ddc-8a07-bbf7ede13f13@oracle.com> <09EDC6BB-BA75-429F-984E-9950E4F0AC67@oracle.com> Message-ID: Now updated. (http://cr.openjdk.java.net/~gbierman/jep359/latest) > On 19 Dec 2019, at 10:45, Gavin Bierman wrote: > > Well spotted Tagir. Updated spec on its way... > >> On 17 Dec 2019, at 14:29, Vicente Romero wrote: >> >> Hi Tagir, >> >> Yes the spec should be updated, you are correct that `parameter` should be added here, >> >> Thanks, >> Vicente >> >> On 12/17/19 5:10 AM, Tagir Valeev wrote: >>> Hello! >>> >>> Current records JLS spec draft [1] says: >>> >>> a record component but T is not applicable to record component >>> declarations, field declarations, method declarations, or type >>> contexts. >>> >>> However, javac doesn't fail if the annotation target is parameter: >>> >>> import java.lang.annotation.*; >>> >>> record Foo(@Anno int x) {} >>> @Target(ElementType.PARAMETER) >>> @interface Anno {} >>> >>> This code compiles and the canonical constructor is annotated with >>> Anno. To me, the javac behavior is expected and spec should be updated >>> to include "parameters": >>> >>> a record component but T is not applicable to record component >>> declarations, field declarations, method declarations, parameters, or >>> type contexts. >>> >>> What do you think? I'm implementing error highlighting for records in >>> IntelliJ now. Should I follow the spec or javac behavior? >>> >>> With best regards, >>> Tagir Valeev. >>> >>> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20191125/specs/records-jls.html#jls-9.7.4 >> > From gavin.bierman at oracle.com Fri Jan 10 13:33:05 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 10 Jan 2020 13:33:05 +0000 Subject: [records] SafeVarargs and canonical constructors In-Reply-To: <70433308.614279.1577646954344.JavaMail.zimbra@u-pem.fr> References: <70433308.614279.1577646954344.JavaMail.zimbra@u-pem.fr> Message-ID: Spec now updated as per this suggestion (http://cr.openjdk.java.net/~gbierman/jep359/latest) > On 29 Dec 2019, at 19:15, Remi Forax wrote: > > Yes, i agree. > > R?mi > > De: "Brian Goetz" > ?: "Tagir Valeev" , "amber-spec-experts" > Envoy?: Dimanche 29 D?cembre 2019 18:35:08 > Objet: Re: [records] SafeVarargs and canonical constructors > This seems a very practical place to land here. > > On 12/29/2019 12:32 PM, Tagir Valeev wrote: > I have a proposal on how to solve SafeVarargs problem: > > 1. Do not allow annotating record type with SafeVarargs > 2. If the record type has explicit canonical/compact constructor, the > heap pollution warning should be issued on the constructor, rather > than on record header declaration > 3. If explicit canonical/compact constructor is annotated with > SafeVarargs, no warning should be issued. > > So if one has a record declaration with potential heap pollution, and > want to declare that varargs are safe, they must explicitly add an > empty compact constructor and annotate it with SafeVarargs. Given that > this case should be extremely rare in practice, such an amount of > boilerplate doesn't look too big to me. On the other hand, such > solution requires less changes in Java (e.g. no need to allow > SafeVarargs on types). > > What do you think? > > With best regards, > Tagir Valeev. > > On Sat, Dec 28, 2019 at 1:10 PM Tagir Valeev wrote: > Hello! > > More questions about SafeVarargs: > > 1. If we have a compact constructor declared, should the warning be > issued at record header or at compact constructor declaration? To > suppress the warning should I annotate a record type or compact > constructor? > > 2. The same if explicit canonical constructor is declared. > > To me, it seems logical to put SafeVarargs at record type if compact > constructor is present (because the variable arity parameter is not > explicitly written in constructor declaration). However, if explicit > canonical constructor is declared, it seems better to require > SafeVarargs at constructor declaration, rather than a record > declaration. Or place it in either place (but not at both). > > What do you think? > > With best regards, > Tagir Valeev. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 16:03:30 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 11:03:30 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <09D15DA1-225C-4619-944B-97432E2B3140@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> <09D15DA1-225C-4619-944B-97432E2B3140@oracle.com> Message-ID: <3229c456-7204-fadf-0a4a-554ce79481ea@oracle.com> I don't want to dive into a design discussion on declared patterns, but I think we can resolve most of the nullability issues without doing so. Like methods, declared patterns are members.? Like methods, they can be static or instance. If they are instance, the target is the receiver, and that surely won't work if the receiver is null.? So by definition, instance patterns cannot match null.? Let's set that aside. (Example: an instance pattern is a pattern on a class, like: ??? switch (aString) { ??????? case palindrome(var firstHalf, boolean isOdd): ... ??? } where palindrome is an instance pattern on String.) Declared patterns have a target type (may want a better name), which for an instance pattern is the receiver type, and for a static pattern is part of the declaration.? (For example, `Optional.of(var x)` has a target type of ` Optional`.) We won't even try to match the pattern unless the match target is of the target type for the pattern (it's not an Optional.of(anything) if its not an Optional to begin with.)? So we have to do some sort of test first -- either instanceof (in which case static pattern will never match null, period), or a type pattern match (which would be the more principled choice, and would only admit a null through to the user-written code if the target type of the static pattern were total on the operand of the switch/instanceof.) So it's already a pretty narrow case where a null would be admitted into the user code at all.? Now, the finishing blow: do we really want to ask the authors of the `Optional.of(var x)` and `Optional.empty()` patterns to have to check their target for null?? That would be creating a million new bugs to accommodate a handful of corner cases. Which brings us back to ... Only the top (total) and bottom (null constant) patterns match null.? Guy is happy again.? (And the crowd goes wild.) On 1/9/2020 10:53 PM, John Rose wrote: > On Jan 9, 2020, at 7:04 PM, Brian Goetz wrote: >> I guess that means the stuff I said about "only having to look in two places" for null-handling was wrong; any static (declared) pattern could be nullable. Which brings us back to the place that people didn't like the last time around -- you have to scrutinize the implementations of the patterns associated with all N cases to determine whether this switch will take nulls or not. > Yeah; I wondered if you?d say that. This is a problem with methods of any > kind, isn?t it? Programmers must always wonder what?s inside any given > method call. We have javadoc and @NotNull annotations to help, but it?s > always going to be difficult. > > That said, library patterns will surely be composable from (or along side) > more primitive patterns, and we (perhaps) could also require that the > various pieces and parts of a null-friendly library pattern would visibly > ?let the null in? to the library code, and failing that the null would be > rejected. > > Straw man: Declare that all static patterns are non-total, and exclude > null from them on the same principle as today?s non-total patterns. > This requires a library pattern to be treated as always non-total. > > Straw man: Allow static patterns to explicitly mention the ?receiver? > of the match, as an optional precursor pattern. Key null-transmission > on whether that precursor pattern is total or not, under today?s rules. > By precursor pattern I mean the occurrence of the pattern ?String? > in the following straw man syntaxes: > > // static __Pattern myLibPattern(__MatchTarget Object target, int modes); > switch (o) { > case myLibPattern(0x42): ? > case myLibPattern(__MatchTarget String x, 0x42): ? > case String x & myLibPattern(0x42): ? > case (String x).myLibPattern(0x42): ? // this looks *really* null hostile! > ?etc? > } > > This depends sensitively on the concrete design of library patterns, but > I?m assuming the usual connection points of (a) the value being matched, > (b) the outputs to the match (if any), and (c) additional parameters (?modes?). > > My overall point here is that there are plausible options to keeping the > null under control, even visibly so. And we can stack the deck so that letting > in the null is always accompanied by some telltale signal, not just a tacit > default. > > I think, when we cross this bridge, we can appeal to some combination > of documentation, good style, and explicit structure in the case pattern. > > Here?s another example to suggest some various degrees of freedom, > and some pitfalls: > > static __Pattern ofClass( > __MatchTarget Object x, > Class c, boolean nullOK, > __MatchResult T x2) { > if (c.isInstance(x)) __Match(x2 = c.cast(x)); > else if (nullOK) __Match(x2 = null); > else __Fail; > } // (not the real syntax) > > boolean z = ?; > Object o = ?; > switch (o) { > case Objects.ofClass(String.class, z, var s): return 1; //worst case > case Objects.ofClass(Number.class, true, var n): return 2; //RTFJD > case Objects.ofClass(List.class, false, var x): return 3; //ditto > default: return 0; //NPE here??? > } > >> On 1/9/2020 7:57 PM, John Rose wrote: >>> We haven?t yet moved on to library-defined patterns (?static >>> patterns?). To gaze a bit into the crystal ball: I think it?s >>> likely that those will provide a way to express T? using >>> a library API instead of a language primitive. We should >>> defer building out that last 1% of null support either >>> indefinitely, or until there is a way for libraries to define >>> their own kinds of patterns. In the latter case the cost >>> of adding a nullable pattern is likely to be lower than >>> the alternatives on the table, since library changes are >>> always cheaper than language changes. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 16:22:11 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 11:22:11 -0500 Subject: [records] translation strategy (factory methods) In-Reply-To: References: Message-ID: There is a whole category if "ooh, shiny new language construct, this is an opportunity to slide in some new features."? We saw this with lambdas -- "can you please make the parameters of lambdas implicitly final."? The motivation for such requests is obvious; we wish all parameters could be implicitly final (perhaps with an opt-out), but we can't turn back the clock, so we console ourselves with "well, at least I can have _some_ finality.)? But this consolation is often disappointing, because it creates (a) challenges for migration between existing constructs (anon classes) and the new construct (lambdas), often in both directions, and (b) requires users to keep track of complex, history-driven rules for when they can use a feature ("oops, can't have static fields in inner classes.")? These are often a siren song, and are best avoided. Turning to records, an example of a feature we've rejected on the basis of this siren song:? Named parameter invocation (`new Point(x: 1, y: 2)`.)? Yes, parameter names are part of the API, so this becomes easier for records than constructors/statics (which is easier, in turn, than for instance methods.)? Instead, we specified that record component names are always reified through reflection, so that if/when we ever get to a more general story for named invocation, records will be ready. While the user burden is less in this case, compiling `new R(...)` to a factory invocation feels like pushing on the wrong end of the "constructor rehabilitation" lever, because doing so will create binary compatibility trouble for records that want to migrate back to classes.? (Like with enums, this is not 100% compatible, because of the supertype (Enum/Record), but like with enums, it is not uncommon to hit the wall and want to refactor back to classes.? Let's not make this harder.) So my vote is #0.? But, if we have a story for moving _all_ classes towards factory construction, then we should make sure records play well.? I don't think we have to do anything now for which we'd regret inaction, because whatever problem we have with legacy records, we'll already have with every class ever written. On 1/10/2020 1:36 AM, John Rose wrote: > Another bit of translation strategy: What does ?new R(a,b,c)? compile > to, at the bytecode level? At some point we will cut over to factory methods, > for at least some types (inlines). Should be pre-empt this trend for records, > and mandate that records are *always* created by factory methods? > > The benefit is that records can be evolved to inline types without > recompiling their *clients*. The risk is that (if we don?t take this > option) that we will have records which are constrained to be identity] > types *until all clients are recompiled*. (No I don?t believe that you > can translate ?new;dup;invokespecial? into ?invokestatic?. > Sorry, that's a pipe dream.) This seems sad, given that records are > partially inspired by value types. > > (From my 2012 blog on value types: ?A value type is immutable? The > equals and hashCode operations are strictly component-wise.? Kind > of like a record. Meanwhile, inlines in Valhalla give component-wise > meaning to acmp not equals and identityHashCode not hashCode.) > > I see three options regarding factories (apart from the above pipe dream): > > 0. Do nothing. Records are just abbreviated classes. Whatever works > or doesn?t for evolving general identity classes to inline classes works > or doesn?t for records. No special benefit, in this vein, to declaring > something a record. > > 1. Define a factory, on top of today?s JVM. Records use a hidden API, > desugaring ?new R(a,b)? to a static ?R.$make$(a,b)?. > > 2. Define a factory, anticipating Valhalla, using a JVM-defined entry point. > The expression ?new R(a,b)? compiles to ?invokestatic R.(int,int)R?, > preceded by a push of a and b. This requires a (relatively shallow) change > to the JVM to double down on the name ?? as the factory behind a > constructor. > > The advantage of 2 is that, if we correctly predict that ?? is the claimed > factory method for value types, then records can be evolved to inlines out > of the box. The advantage of 1 is that we don?t need to make that prediction. > > There?s also this: > > 3. Do 1. but when 2. becomes an option create an auto-bridge from ?$make$? > to ??. New code doesn?t need the bridge because it compiles to call > ?? out of the box. > > Here?s a gut check for this group: Are we confident enough with Valhalla > that we can settled on ?invokestatic ? as the new dance for creating > an instance that may or may not be an inline? > > ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Jan 10 16:30:19 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 10 Jan 2020 23:30:19 +0700 Subject: [records] New draft spec for JEP 359 In-Reply-To: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> Message-ID: Woo-hoo, a new year present, spec update! I don't see any changes regarding the variable arity mismatch between the canonical constructor and record header. See my e-mail [1]. Was this suggestion rejected or overlooked? Also, a cosmetic suggestion in 8.10.1: It can be seen that the production for RecordComponent is identical in content to the production for FormalParameter (8.4.1), except a record component can not have a final modifier. The following productions from 8.4.1, 8.3 and 4.3 are shown here for convenience: FormalParameter:... VariableArityParameter:... VariableModifier:... VariableDeclaratorId... Dims:... Now, if we don't allow any modifiers anymore, displaying here FormalParameter, VariableArityParameter and VariableModifier don't add any convenience and probably adds only some confusion (one may see VariableModifier production and mistakenly assume that 'final' is allowed). I would leave here only productions referenced from the record header grammar: VariableDeclaratorId and Dims. Finally, a minor nitpick regarding 8.1.4 Superclasses and subclasses: > It is a compile-time error if the ClassType names the class Enum or any invocation of Enum (8.9). > It is a compile-time error if the ClassType names the class Record (8.10). Here Enum and Record are referred by simple name while in other places of the spec they are referred via fully-qualified name. I feel this adds some ambiguity, though it's a minor point. Everything else looks great to me, and I'm happy to see that most of our recent discussions are crystallized in the spec now. Thank you! With best regards, Tagir Valeev. [1] http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html On Fri, Jan 10, 2020 at 8:32 PM Gavin Bierman wrote: > > Happy new year everyone! An updated version of the records spec is available at: > > http://cr.openjdk.java.net/~gbierman/jep359/latest > > (which currently points to http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) > > This addresses: > > * Removes `final` modifier on record components > * Adding missing annotation target PARAMETER > * Corrects discussion of @SafeVarargs annotation > * Forbids capture of local variables in a local record > * Clarifies implicit formal parameter list in a compact constructor > > Gavin > > > From amaembo at gmail.com Fri Jan 10 16:33:13 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 10 Jan 2020 23:33:13 +0700 Subject: [records] translation strategy (factory methods) In-Reply-To: References: Message-ID: Agreed with Brian. With best regards, Tagir Valeev. On Fri, Jan 10, 2020 at 11:22 PM Brian Goetz wrote: > > There is a whole category if "ooh, shiny new language construct, this is an opportunity to slide in some new features." We saw this with lambdas -- "can you please make the parameters of lambdas implicitly final." The motivation for such requests is obvious; we wish all parameters could be implicitly final (perhaps with an opt-out), but we can't turn back the clock, so we console ourselves with "well, at least I can have _some_ finality.) But this consolation is often disappointing, because it creates (a) challenges for migration between existing constructs (anon classes) and the new construct (lambdas), often in both directions, and (b) requires users to keep track of complex, history-driven rules for when they can use a feature ("oops, can't have static fields in inner classes.") These are often a siren song, and are best avoided. > > Turning to records, an example of a feature we've rejected on the basis of this siren song: Named parameter invocation (`new Point(x: 1, y: 2)`.) Yes, parameter names are part of the API, so this becomes easier for records than constructors/statics (which is easier, in turn, than for instance methods.) Instead, we specified that record component names are always reified through reflection, so that if/when we ever get to a more general story for named invocation, records will be ready. > > While the user burden is less in this case, compiling `new R(...)` to a factory invocation feels like pushing on the wrong end of the "constructor rehabilitation" lever, because doing so will create binary compatibility trouble for records that want to migrate back to classes. (Like with enums, this is not 100% compatible, because of the supertype (Enum/Record), but like with enums, it is not uncommon to hit the wall and want to refactor back to classes. Let's not make this harder.) > > So my vote is #0. But, if we have a story for moving _all_ classes towards factory construction, then we should make sure records play well. I don't think we have to do anything now for which we'd regret inaction, because whatever problem we have with legacy records, we'll already have with every class ever written. > > > > > On 1/10/2020 1:36 AM, John Rose wrote: > > Another bit of translation strategy: What does ?new R(a,b,c)? compile > to, at the bytecode level? At some point we will cut over to factory methods, > for at least some types (inlines). Should be pre-empt this trend for records, > and mandate that records are *always* created by factory methods? > > The benefit is that records can be evolved to inline types without > recompiling their *clients*. The risk is that (if we don?t take this > option) that we will have records which are constrained to be identity] > types *until all clients are recompiled*. (No I don?t believe that you > can translate ?new;dup;invokespecial? into ?invokestatic?. > Sorry, that's a pipe dream.) This seems sad, given that records are > partially inspired by value types. > > (From my 2012 blog on value types: ?A value type is immutable? The > equals and hashCode operations are strictly component-wise.? Kind > of like a record. Meanwhile, inlines in Valhalla give component-wise > meaning to acmp not equals and identityHashCode not hashCode.) > > I see three options regarding factories (apart from the above pipe dream): > > 0. Do nothing. Records are just abbreviated classes. Whatever works > or doesn?t for evolving general identity classes to inline classes works > or doesn?t for records. No special benefit, in this vein, to declaring > something a record. > > 1. Define a factory, on top of today?s JVM. Records use a hidden API, > desugaring ?new R(a,b)? to a static ?R.$make$(a,b)?. > > 2. Define a factory, anticipating Valhalla, using a JVM-defined entry point. > The expression ?new R(a,b)? compiles to ?invokestatic R.(int,int)R?, > preceded by a push of a and b. This requires a (relatively shallow) change > to the JVM to double down on the name ?? as the factory behind a > constructor. > > The advantage of 2 is that, if we correctly predict that ?? is the claimed > factory method for value types, then records can be evolved to inlines out > of the box. The advantage of 1 is that we don?t need to make that prediction. > > There?s also this: > > 3. Do 1. but when 2. becomes an option create an auto-bridge from ?$make$? > to ??. New code doesn?t need the bridge because it compiles to call > ?? out of the box. > > Here?s a gut check for this group: Are we confident enough with Valhalla > that we can settled on ?invokestatic ? as the new dance for creating > an instance that may or may not be an inline? > > ? John > > From brian.goetz at oracle.com Fri Jan 10 16:38:46 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 11:38:46 -0500 Subject: [records] updates for Preview 6. Default access modes In-Reply-To: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> Message-ID: <9397313e-f667-f4bd-39ca-e29f0cd0de82@oracle.com> As with the previous mail, this is one of those where we have to trade off between what is ideal for the purpose and not introducing gratuitous new variations between similar looking entities, which add to the developer's complexity load and create migration challenges. We've already discussed making the default accessibility for mandated members match that of the class, rather than "public." What you're asking for is: when someone explicitly specifies a mandated member, they not have to say the access modifier. I sympathize, but I think this would be a mistake.? First, the idea is that most of the time, you don't have to explicitly specify the mandated members.? But more importantly, this is a string that you can pull on arbitrarily long.? Suppose the default accessibility for explicit mandated members were the "right thing" (either public, or matching the class -- the difference is purely aesthetic.)? Now there's a gap between these members and others, that sucks.? So suppose all members had that access mode by default (as you propose in B).? This at least has the benefit that it is like interfaces, rather than a new thing, but ... interfaces cannot use certain accessibilities (no protected, no private fields or member classes), so its not exactly the same.? But more importantly, this creates (a) friction between records and classes (I disagree that records are more like interfaces -- and more importantly, I think most users will think of them as compact classes), and (b) migration friction.? All to save a few "publics" -- but only a vanishingly small percentage of the publics in a typical code base. I think we're back in the "aha, a chance to fix mistakes of the past" trap. On 1/10/2020 1:40 AM, John Rose wrote: > 6. Default access modes in records. (See above. ) > > I specifically want to make sure we are doing the best thing on > access defaults (what happens when you don?t mention *any* > access mode). I suppose that?s a separate item, related to this > one. Even though we have a decision of record on this, I?m > calling it out here and now because we are stuck with the final > decision we make this time around. (Maybe like a mandatory > appeal for a capital case.) > > Choices: A. Package scope like classes, B. public like interfaces, > C,D,? something new for records. > > A. Records are concise classes which represent a state vector > of components, plus optional additional behavior. If a record > member is declared with no access mode, it is given the package > access mode. Just like classes. The word ?public? must be > reaffirmed for every API in the record; just like classes, the > presumption is that API points should be private by default, > and only made public via explicit mark-up. > > B. Records are state vectors of components, plus optional > additional behavior. They are not general classes, but rather > constrained types, much like interfaces are behavior vectors > of abstract API points, plus optional behavior. If a record > member is declared with no access mode, it is given the > public access mode. Just like an interface, a record is a > restricted type, whose contracts with the outside world > are mainly public, and which has a limited capacity for > non-public contracts. > > C. Records are state vectors of components, plus optional > additional behavior. The parts of their API which are > intended to be mostly public are public by default, at least > if the record type as a whole is public. The precedent for > this variability is the implicit empty nullary constructor > supplied to every class, whose access mode is copied > from the class itself. The parts of a record API that are > expected to be public are the factory (canonical constructor) > and the accessors (component projection methods). > So those guys are public by default (if the record is public). > (If the record isn?t public those other guys are the same.) > > At present I prefer ?B?, because I now think that, regarding > the access expectiations of API points, records are *far more > similar to interfaces* than they are to random run of the mill > classes. We started with random classes, but after applying > enough constraints we now have API elements which are > closely analogous to interfaces: Bundles of state, bundles > of operations. And, for wide use; i.e. few or no secrets. > In both cases, ?public? is the sane default. > > An advantage of ?B? is there?s nothing new to teach. ?You > know how interfaces have their own default access? Well, > records use the same rules.? > > ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 16:42:11 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 11:42:11 -0500 Subject: equals/hashCode/toString implementation In-Reply-To: <558986163.785925.1578654840364.JavaMail.zimbra@u-pem.fr> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <558986163.785925.1578654840364.JavaMail.zimbra@u-pem.fr> Message-ID: > > sure ! > toString is very very very slow, equals can be a little bit faster, > hashCode has no issue. toString is a known issue; we have a separate project to speed up String.format() across the board, so we didn't do anything special for toString here.? But I'm sure the bootstrap can be improved independently, if someone wanted to take a shot at it.? Delegating to concat factory is a good option.? (Patch please!) > > - equals doesn't test primitive first so it's just a little bit slower > that if I write it by hand This should be a matter of just sorting the tests by primitive/non, right?? This should also be an easy thing to improve, now or later. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 16:44:17 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 11:44:17 -0500 Subject: [records] updates for Preview 6. Default access modes In-Reply-To: References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> Message-ID: <7dde8ff4-f5d3-fed1-0f86-dd53764238a1@oracle.com> This is where we want to aim for.? It was not practical (for reasons of accidental-complexity management) in preview/1 but those issues have been addressed and it is now practical to get there. On 1/10/2020 5:04 AM, Tagir Valeev wrote: > Of these options, I like C the most, though I would modify it: > D. The access modifier for explicit overloads to synthetic members > (canonical constructor & accessors) must be the same or more > permissive than an access modifier for the record itself. The absence > of access modifier means 'package-private', as usual for classes. This > would require 'public' for public records, and it's nice because you > will see that it's really a part of the API. On the other hand, this > would reduce noise for inner/local records where you can omit the > access modifier. And still, you can specify public on local records, > so you can easily reduce the record visibility without the need to > modify all the members. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 17:01:22 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 12:01:22 -0500 Subject: [records] translation strategy In-Reply-To: References: Message-ID: <37041716-2dd5-a758-1adb-3f3e4c65deb9@oracle.com> A significant reason to have a supertype is not API points, but API specs.? We can refine the spec of equals() if there is an abstract Record type. On 1/10/2020 12:09 AM, John Rose wrote: > A. Specify a super type now, and add the new API connection points to > the super type. It could be an interface or an abstract class. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 17:03:47 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 12:03:47 -0500 Subject: [records] New draft spec for JEP 359 In-Reply-To: References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> Message-ID: > I don't see any changes regarding the variable arity mismatch between > the canonical constructor and record header. See my e-mail [1]. Was > this suggestion rejected or overlooked? We might also want to just outlaw the old-style array declaration for components: ??? record Foo(int a[]) // error ??? record Foo(int[] a) // good From guy.steele at oracle.com Fri Jan 10 17:12:20 2020 From: guy.steele at oracle.com (Guy Steele) Date: Fri, 10 Jan 2020 12:12:20 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <3229c456-7204-fadf-0a4a-554ce79481ea@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <7C5866F9-53AA-4BC6-8A43-1199F0CB32E0@oracle.com> <0db2e305-99fe-890c-b2ef-134dfec733a3@gmail.com> <1EB997D4-9691-482C-AC58-FCAE104406ED@oracle.com> <1158d88e-3f68-3b94-1506-d4f09616e1f3@oracle.com> <09D15DA1-225C-4619-944B-97432E2B3140@oracle.com> <3229c456-7204-fadf-0a4a-554ce79481ea@oracle.com> Message-ID: <5CD83364-7C9F-4BA0-BA02-AC4A3246978D@oracle.com> > On Jan 10, 2020, at 11:03 AM, Brian Goetz wrote: > > I don't want to dive into a design discussion on declared patterns, but I think we can resolve most of the nullability issues without doing so. > > Like methods, declared patterns are members. Like methods, they can be static or instance. > > If they are instance, the target is the receiver, and that surely won't work if the receiver is null. So by definition, instance patterns cannot match null. Let's set that aside. > > (Example: an instance pattern is a pattern on a class, like: > > switch (aString) { > case palindrome(var firstHalf, boolean isOdd): ... > } > > where palindrome is an instance pattern on String.) > > Declared patterns have a target type (may want a better name), which for an instance pattern is the receiver type, and for a static pattern is part of the declaration. (For example, `Optional.of(var x)` has a target type of ` Optional`.) > > We won't even try to match the pattern unless the match target is of the target type for the pattern (it's not an Optional.of(anything) if its not an Optional to begin with.) So we have to do some sort of test first -- either instanceof (in which case static pattern will never match null, period), or a type pattern match (which would be the more principled choice, and would only admit a null through to the user-written code if the target type of the static pattern were total on the operand of the switch/instanceof.) > > So it's already a pretty narrow case where a null would be admitted into the user code at all. Now, the finishing blow: do we really want to ask the authors of the `Optional.of(var x)` and `Optional.empty()` patterns to have to check their target for null? That would be creating a million new bugs to accommodate a handful of corner cases. > > Which brings us back to ... > > Only the top (total) and bottom (null constant) patterns match null. Guy is happy again. (And the crowd goes wild.) Nice bit of reasoning, and I am indeed happy again. (I was hoping I would have to write ?switch?? exactly once in my life. Well, this makes twice. But this isa much better solution.) ?Guy -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 18:07:44 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 13:07:44 -0500 Subject: [records] New draft spec for JEP 359 In-Reply-To: References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> Message-ID: <8c36c7a5-1966-5079-d404-b50467d1a8dd@oracle.com> On 1/10/2020 11:30 AM, Tagir Valeev wrote: > I don't see any changes regarding the variable arity mismatch between > the canonical constructor and record header. See my e-mail [1]. Was > this suggestion rejected or overlooked? This can be accomplished by adding ?- If the record is a varargs record, the canonical constructor must be a varargs constructor; ?- If the record is not a varargs record, the canonical constructor must not be a varargs constructor (modulo terminology) to > If a canonical constructor is explicitly declared, then it must > additionally satisfy the following conditions; otherwise a > compile-time error occurs: > > * > > The names and types of the formal parameters in the formal > parameter list of the canonical constructor must be identical to > the names and declared type of the corresponding record component. > > * > > A canonical constructor must not be generic (8.8.4 > ). > > * > > A canonical constructor must be declared|public|. > > * > > A canonical constructor must not have a|throws|clause. > > * > > The body of a canonical constructor must not contain an explicit > constructor invocation statement (8.8.7.1 > ). > > * > > All the other rules for a constructor in a normal class > declaration must be satisfied (8.8 > ). > -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Fri Jan 10 19:12:34 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 10 Jan 2020 11:12:34 -0800 Subject: [records] translation strategy (factory methods) In-Reply-To: References: Message-ID: <8E61BFEF-6EE5-4C9F-BEEE-039CD2139E74@oracle.com> On Jan 10, 2020, at 8:22 AM, Brian Goetz wrote: > > So my vote is #0. But, if we have a story for moving _all_ classes towards factory construction, then we should make sure records play well. I don't think we have to do anything now for which we'd regret inaction, because whatever problem we have with legacy records, we'll already have with every class ever written. That?s fair. And good observations about the siren song. The main downside is that records will be hard to convert to inlines without client recompilation, unless we create some as yet unknown tech. for morphing the new/dup/invoke dance into a factory call. (The problem is doing something with the result of the ?new? instruction so it ends up being an immutable inline reference at the end of the dance, but during the dance it must somehow absorb side effects from the invoke. And the dance is not always a simple and local bytecode pattern; stuff like `new X(new Y(z))` is not local in shape.) But I agree that this is a larger problem than just records; many non-record classes are potentially migratable to inlines. So there?s limited value, and real irregularity, for special pleading on records. (OK, we?ve got that on record.) From john.r.rose at oracle.com Fri Jan 10 19:15:04 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 10 Jan 2020 11:15:04 -0800 Subject: [records] translation strategy In-Reply-To: <37041716-2dd5-a758-1adb-3f3e4c65deb9@oracle.com> References: <37041716-2dd5-a758-1adb-3f3e4c65deb9@oracle.com> Message-ID: On Jan 10, 2020, at 9:01 AM, Brian Goetz wrote: > > A significant reason to have a supertype is not API points, but API specs. We can refine the spec of equals() if there is an abstract Record type. Agreed. I suppose these observations about translation strategy provide an independent motivation for using a real record super. But, yes, a bigger value of a super is its factored and discoverable javadoc. From vicente.romero at oracle.com Fri Jan 10 19:29:38 2020 From: vicente.romero at oracle.com (Vicente Romero) Date: Fri, 10 Jan 2020 14:29:38 -0500 Subject: [records] New draft spec for JEP 359 In-Reply-To: <8c36c7a5-1966-5079-d404-b50467d1a8dd@oracle.com> References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> <8c36c7a5-1966-5079-d404-b50467d1a8dd@oracle.com> Message-ID: On 1/10/20 1:07 PM, Brian Goetz wrote: > > > On 1/10/2020 11:30 AM, Tagir Valeev wrote: >> I don't see any changes regarding the variable arity mismatch between >> the canonical constructor and record header. See my e-mail [1]. Was >> this suggestion rejected or overlooked? > > This can be accomplished by adding > > ?- If the record is a varargs record, the canonical constructor must > be a varargs constructor; > ?- If the record is not a varargs record, the canonical constructor > must not be a varargs constructor let's not forget that we will have a different syntax in the accessor `[]` so we could make the syntax the same for the canonical constructor but then we will leak at the accessor Vicente > > (modulo terminology) > > to > >> If a canonical constructor is explicitly declared, then it must >> additionally satisfy the following conditions; otherwise a >> compile-time error occurs: >> >> * >> >> The names and types of the formal parameters in the formal >> parameter list of the canonical constructor must be identical to >> the names and declared type of the corresponding record component. >> >> * >> >> A canonical constructor must not be generic (8.8.4 >> ). >> >> * >> >> A canonical constructor must be declared|public|. >> >> * >> >> A canonical constructor must not have a|throws|clause. >> >> * >> >> The body of a canonical constructor must not contain an explicit >> constructor invocation statement (8.8.7.1 >> ). >> >> * >> >> All the other rules for a constructor in a normal class >> declaration must be satisfied (8.8 >> ). >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 20:00:47 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 15:00:47 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> Message-ID: <01584425-2b4d-315a-b16c-d13e47464573@oracle.com> Closing the loop, this raises the question of "what about instanceof and total patterns"?? I posit that the following locutions are silly: ??? if (e instanceof var x) { ... }??? // always true ??? if (e instanceof _) { ... } ?? ??? // always true and probably should be banned.? If we did, though, what about: ??? if (e instanceof Object o) { ... } // always true if (e instanceof Object) { ... }?? // always true, but currently allowed I would think we would ban the former as well, but we have to keep the latter around for compatibility.? (Which is partially why I discouraged calling the latter an "anonymous pattern" in the spec, and instead proposed to treat it as a different flavor of `instanceof`.) SO, proposed: disallow "any" patterns (_, var x, or total T x) in instanceof. Instanceof is for partial patterns. Note that ??? Point p; ??? if (p instanceof Point(var x, var y)) { } is total, but we would't want to disallow it, as this pattern could still fail if p == null. We might want to go a little further, and ban constant patterns in instanceof too, since all of the following have simpler forms: ??? if (x instanceof null) { ... } ??? if (x instanceof "") { ... } ??? if (i instanceof 3) { ... } Or not -- I suspect not. On 1/8/2020 3:27 PM, Brian Goetz wrote: > In the past, we've gone around a few times on nullability and pattern > matching.? Back when we were enamored of `T?` types over in Valhalla > land, we tentatively landed on using `T?` also for nullable type > patterns.? But the bloom came off that rose pretty quickly, and > Valhalla is moving away from it, and that makes it far less attractive > in this context. > > There are a number of tangled concerns that we've tried a few times to > unknot: > > ?- Construct nullability.? Constructs to which we want to add pattern > awareness (instanceof, switch) already have their own opinion about > nulls.? Instanceof always says false when presented with a null, and > switch always NPEs. > > ?- Pattern nullability.? Some patterns clearly would never match null > (deconstruction patterns), whereas others (an "any" pattern, and > surely the `null` constant pattern, if there was one) might make sense > to match null. > > ?- Nesting vs top-level.? Most of the time, we don't want to match > null at the top level, but frequently in a nested position we do. This > conflicts with... > > ?- Totality vs partiality.? When a pattern is partial on the operand > type (e.g., `case String` when the operand of switch is `Object`), it > is almost never the case we want to match null (well, except for the > `null` constant pattern), whereas when a pattern is total on the > operand type (e.g., `case Object` in the same example), it is more > justifiable to match null. > > ?- Refactoring friendliness.? There are a number of cases that we > would like to freely refactor back and forth (e.g., if-instanceof > chain vs pattern switch).? In particular, refactoring a switch on > nested patterns to a nested switch (case Foo(T t), case Foo(U u) to a > nested switch on T and U) is problematic under some of the > interpretations of nested patterns. > > ?- Inference.? It would be nice if a `var` pattern were simply > inference for a type pattern, rather than some possibly-non-denotable > union.? (Both Scala and C# treat these differently, which means you > have to choose between type inference and the desired semantics; I > don't want to put users in the position of making this choice.) > > > Let's try (again) to untangle these.? A compelling example is this one: > > ??? Box box; > ??? switch (box) { > ??????? case Box(Chocolate c): > ??????? case Box(Frog f): > ??????? case Box(var o): > ??? } > > It would be highly confusing and error-prone for either of the first > two patterns to match Box(null) -- given that Chocolate and Frog have > no type relation (ok, maybe they both implement `Edible`), it should > be perfectly safe to reorder the two.? But, because the last pattern > is so obviously total on boxes, it is quite likely that what the > author wants is to match all remaining boxes, including those that > contain null. (Further, it would be super-bad if there were _no_way to > say "Match any Box, even if it contains null.? While one might think > this could be repaired with OR patterns, imagine that `Box` had N > components -- we'd need to OR together 2^n patterns, with complex > merging, to express all the possible combinations of nullity.) > > Scala and C# took the path of saying that "var" patterns are not just > type inference, they are "any" patterns -- so `Box(Object o)` matches > boxes containing a non-null payload, where `Box(var o)` matches all > boxes.? I find this choice to be both questionable (the story that > `var` is just inference is nice) and also that it puts users in the > position of having to choose between the semantics they want and being > explicit about types.? I see the expedience of it, but I do not think > this is the right answer for Java. > > > In the previous round, we posited that there were _type > patterns_(denoted `T t`) and _nullable type patterns_(denoted `T? t`), > which had the advantage that you could be explicit about what you > wanted (nulls or not), and which was sort of banking on Valhalla > plunking for the `T? ` notation.? But without that, only having `T?` > in patterns, and no where else, will stick out like a sore thumb. > > There are many ways to denote "T or null", of course: > > ?- Union types: `case (T|Null) t` > ?- OR patterns: `case (T t) | (Null t)`, or `case (T t) | (null t)` > (the former is a union with a null TYPE pattern, the latter with a > null CONSTANT pattern) > ?- Merging/fallthrough: `case T t, Null t` > ?- Some way to spell "nullable T": `case T? t`, `case nullable T t`, > `case T|null t` > > But, I don't see any of these as being all that attractive in the Box > case, when the most likely outcome is that the user wants the last > case to match all boxes. > > > Here's a scheme that I think is workable, which we hovered near > sometime in the past, and which I want to go back to. We'll start with > the observation that `instanceof` and `switch` are currently hostile > to nulls (instanceof says false, switch throws, and probably in the > future, let/bind will do so also.) > > ?- We accept that some constructs may have legacy hostility to nulls > (but, see below for a possible relaxation); > ?- There are no "nullable type patterns", just type patterns; > - Type patterns that are _total_ on their target operand (`case T` on > an operand of type `U`, where `U <: T`) match null, and non-total type > patterns do not. > ?- Var patterns can be considered "just type inference" and will mean > the same thing as a type pattern for the inferred type. > > In this world, the patterns that match null (if the construct allows > it through) are `case null` and the total patterns -- which could be > written `var x` (and maybe `_`, or maybe not), or `Object x`, or even > a narrower type if the operand type is narrower. > > In our Box example, this means that the last case (whether written as > `Box(var o)` or `Box(Object o)`) matches all boxes, including those > containing null (because the nested pattern is total on the nested > operand), but the first two cases do not. > > An objection raised against this scheme earlier is that readers will > have to look at the declaration site of the pattern to know whether > the nested pattern is total. This is a valid concern (to be traded off > against the other valid concerns), but this does not seem so bad in > practice to me -- it will be common to use var or other broad type, in > which case it will be obvious.) > > One problem with this interpretation is that we can't trivially > refactor from > > ??? switch (o) { > ??????? case Box(Chocolate c): > ??????? case Box(Frog f): > ??????? case Box(var o): > ??? } > > to > > ??? switch (o) { > ??????? case Box(var contents): > ??????????? switch (contents) { > ??????????????? case Chocolate c: > ??????????????? case Frog f: > ??????????????? case Object o: > ??????????? } > ??????? } > ??? } > > because the inner `switch(contents)` would NPE, because switch is > null-hostile.? Instead, the user would explicitly have to do an `if > (contents == null)` test, and, if the intent was to handle null in the > same way as the bottom case, some duplication of code would be > needed.? This is irritating, but I don't think it is disqualifying -- > it is in the same category of null irritants that we have throughout > the language. > > Similarly, we lose the pleasing decomposition that the nested pattern > `P(Q)` is the same pattern as `P(alpha) & alpha instanceof Q` when P's > 1st component might be null and the pattern Q is total -- because of > the existing null-hostility of `instanceof`.? (This is not unlike the > complaint that Optional doesn't follow the monad law, with a similar > consequence -- and a similar justification.) > > So, summary: > ?- the null constant pattern matches null; > ?- "any" patterns match null; > ?- A total type pattern is an "any" pattern; > ?- var is just type inference; > ?- no other patterns match null; > ?- existing constructs retain their existing null behaviors. > > > I'll follow up with a separate message about switch null-hostility. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Fri Jan 10 21:00:15 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 10 Jan 2020 13:00:15 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: <01584425-2b4d-315a-b16c-d13e47464573@oracle.com> References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <01584425-2b4d-315a-b16c-d13e47464573@oracle.com> Message-ID: On Jan 10, 2020, at 12:00 PM, Brian Goetz wrote: > > SO, proposed: disallow "any" patterns (_, var x, or total T x) in instanceof. Instanceof is for partial patterns. +1. Total patterns belong at the bottom of a decision tree, not in an instanceof. Hmm. This affects desugaring of switch. Maybe the final, total case of a switch is desugared by a let instead of an if/instanceof. It makes the desugaring depend on whether the final case is total or not, which depends on types. I think that?s an OK thing to do; it makes clear how totality affects translation. > Note that > > Point p; > if (p instanceof Point(var x, var y)) { } > > is total, but we would't want to disallow it, as this pattern could still fail if p == null. Ooh, that?s a tricky corner case. There?s an argument for saying ?that?s total *and* null-hostile?, by analogy with ?if (anInteger == 0)?. But I suppose instanceof should never throw, so returning false for null there is OK. > We might want to go a little further, and ban constant patterns in instanceof too, since all of the following have simpler forms: > > if (x instanceof null) { ... } > if (x instanceof "") { ... } > if (i instanceof 3) { ... } > > Or not -- I suspect not. One reason to keep it is floats: if (x instanceof Float.NaN) { ? } This seems to be a fine thing for an IDE to warn about, and not so fine for a language to legislate. ? John From guy.steele at oracle.com Fri Jan 10 21:18:59 2020 From: guy.steele at oracle.com (Guy Steele) Date: Fri, 10 Jan 2020 16:18:59 -0500 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <01584425-2b4d-315a-b16c-d13e47464573@oracle.com> Message-ID: > On Jan 10, 2020, at 4:00 PM, John Rose wrote: > > On Jan 10, 2020, at 12:00 PM, Brian Goetz wrote: >> . . . > >> We might want to go a little further, and ban constant patterns in instanceof too, since all of the following have simpler forms: >> >> if (x instanceof null) { ... } >> if (x instanceof "") { ... } >> if (i instanceof 3) { ... } >> >> Or not -- I suspect not. > > One reason to keep it is floats: > > if (x instanceof Float.NaN) { ? } > > This seems to be a fine thing for an IDE to warn about, and not > so fine for a language to legislate. Well, if the _only_reason to allow instanceof with a constant is floats and doubles (comparing to NaN is the one thing that == or .equals might not handle properly), then it?s not a very good reason; I dare say Float.isNan(x) is the first thing that would jump to mind, and it would be a whole lot more readable as well. ?Guy From brian.goetz at oracle.com Fri Jan 10 21:20:57 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 16:20:57 -0500 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> Message-ID: <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> No objection to further hashing the specification of the hash.? Care to post a proposed patch for the spec in j.l.Record? On 1/9/2020 11:45 PM, John Rose wrote: > If we don?t decide hashCode explicitly, I think we?ll back ourselves into > a bad default decision. Should the hashing logic for r.hashCode() be specified > in a portable manner? I say, ?no?, since there?s no good portable solution. > Objects.hash is portable, but other than that is a poor algorithm. > > Right now we don?t *specify* that we combine hashes using the base-31 > polynomial, but we *do* use that stupid old thing. This is plausibly conservative > but it will get it into a worse spot than necessary. We can buy ourselves some > time by putting weasel words into Record::hashCode (or its successor) and > ObjectMethods::bootstrap. We can promise that the hashCode result > depends (typically) on all components of the record, and on nothing else. > We should also explicitly state that the method for combining the bits is > subject to change, very much like we specify that the iteration order for > Set.of and Map.of is subject to change, like this: > > http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/Set.java#l83 > > Finally, for luck, we should add a pinch of salt to the hash function > to keep users on their toes, like this: > > http://hg.openjdk.java.net/jdk/jdk/file/e9c11ba790b2/src/java.base/share/classes/java/util/ImmutableCollections.java#l619 > > (It?s an implementation detail, but for now this combiner would be enough > to buy us a better future: `(h,x) -> ((h*31)+x)^SALT`) > > If we aren?t cautious about this, I?m afraid that users will eventually discover > that we are using the Objects.hash algorithm, we will be pressured to > document it, and then we?ll be locked out from an easy improvement to > records. It?s easier to introduce the salt before that happens. > > I don?t think we should even promise that the record hashCode is mixed > from the hashCodes of the components. That?s TMI, and it boxes us in > without telling the user anything useful. All we need to do is promise > that a record type?s hashCode fulfills the Object contract, especially > relative to the record?s equals method. No other promises, plus a warning > that result is subject to change. And then rub some salt on it to keep > it clean, as with Set.of and Map.of. > > ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Fri Jan 10 21:38:18 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 10 Jan 2020 14:38:18 -0700 Subject: Towards cleaner nesting In-Reply-To: <3e03bd62-a17f-ff94-031f-ef38aa1797b7@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> <3e03bd62-a17f-ff94-031f-ef38aa1797b7@oracle.com> Message-ID: <2A9C954C-ABF9-49C2-A85A-946414FF51B9@oracle.com> > On Jan 9, 2020, at 8:02 PM, Brian Goetz wrote: > > So variable initializers can have expressions in them, which can have statements in them My mental model was to fudge and call those "instance initializers". :-) (And then I debated dropping the asterisk on instance/local variables, and didn't, but I guess for consistency should have, because the initializers are the only place variable references can live.) >> - interfaces can't directly contain instance initializers/constructors, instance/local variables, or inner classes (maybe) > In order to have inner classes in an interface, we'd have to have a non-static modifier, since nested classes in interfaces are allowed now and are implicitly static. Yep. But letting syntax drive this decision doesn't seem very much in the spirit of "anything can be nested in anything", so maybe new syntax is worth it to iron out this wrinkle? Or not?maybe "no new syntax" is where we draw the line. >> - annotation types can't directly contain things prohibited in interfaces, or instance/local methods (maybe) > - currently annos cannot have fields, default methods, or static methods -- this still seems a reasonable restriction. Why? I mean, I don't really care all that much, but if we're removing ad hoc restrictions, this one (no methods) certainly seems ad hoc. We don't even need new syntax. > Your list doesn't treat constructors, either as "what can contain them" (pretty clear that's classes/records/enums only), or as "what can they contain" (could a constructor have, for example, a local method or a local class.) It does, in fact. But lumped in with instance initializers, as a kind of "method declaration", so you probably overlooked it. As a starting point, we can say anything can contain a constructor, and constructors can contain anything. And then there are restrictions: - interfaces/annotations/methods of any kind can't contain them - they can't contain things prohibited in regular methods I don't see any reason to make constructor bodies more restrictive in terms of nesting than method bodies. Local classes are already legal. From brian.goetz at oracle.com Fri Jan 10 21:43:40 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 16:43:40 -0500 Subject: Towards cleaner nesting In-Reply-To: <2A9C954C-ABF9-49C2-A85A-946414FF51B9@oracle.com> References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> <3e03bd62-a17f-ff94-031f-ef38aa1797b7@oracle.com> <2A9C954C-ABF9-49C2-A85A-946414FF51B9@oracle.com> Message-ID: >>> - interfaces can't directly contain instance initializers/constructors, instance/local variables, or inner classes (maybe) >> In order to have inner classes in an interface, we'd have to have a non-static modifier, since nested classes in interfaces are allowed now and are implicitly static. > Yep. But letting syntax drive this decision doesn't seem very much in the spirit of "anything can be nested in anything", so maybe new syntax is worth it to iron out this wrinkle? Or not?maybe "no new syntax" is where we draw the line. It's not just syntax; it's not clear to me whether inner classes of interfaces make enough sense / have enough applicability to warrant such a change.? My thinking was to smooth out the gratuitous restrictions within the model we have, and clean up the language in the spec, and then think about whether there's more to do. > >>> - annotation types can't directly contain things prohibited in interfaces, or instance/local methods (maybe) >> - currently annos cannot have fields, default methods, or static methods -- this still seems a reasonable restriction. > Why? I mean, I don't really care all that much, but if we're removing ad hoc restrictions, this one (no methods) certainly seems ad hoc. We don't even need new syntax. Because annotations are supposed to be pure metadata.? These things all pull state and behavior into something that is supposed to be pure metadata.?? I mean, sure, I can imagine a world that blurs the distinction between annotations and interfaces, but I'm not seeing these restrictions as _gratuitous_; they were made deliberately. From john.r.rose at oracle.com Fri Jan 10 21:55:13 2020 From: john.r.rose at oracle.com (John Rose) Date: Fri, 10 Jan 2020 13:55:13 -0800 Subject: [patterns] Nullability in patterns, and pattern-aware constructs (again) In-Reply-To: References: <490f109c-5fea-2b78-93ea-3198fcdcdfcd@oracle.com> <01584425-2b4d-315a-b16c-d13e47464573@oracle.com> Message-ID: <23A55621-6D58-41CD-9EDD-0A0F8FCF1BF8@oracle.com> On Jan 10, 2020, at 1:18 PM, Guy Steele wrote: > Well, if the _only_reason to allow instanceof with a constant is floats and doubles (comparing to NaN is the one thing that == or .equals might not handle properly), then it?s not a very good reason; I dare say Float.isNan(x) is the first thing that would jump to mind, and it would be a whole lot more readable as well. The refactoring story is another reason: Allowing instanceof simplifies refactoring, and also any spec that appeals to desugaring. A complicated pattern might contain a constant K somewhere. It?s surely better to desugar uniformly to instanceof applications, and *then* note that in some cases x instanceof K can be further desugared to (Float.isNan(K) ? Float.isNan(x) : x == K), when x is a float. From forax at univ-mlv.fr Fri Jan 10 22:19:07 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 10 Jan 2020 23:19:07 +0100 (CET) Subject: Component deprecation (was: [records] Record updates for Preview/2) In-Reply-To: References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> Message-ID: <1739217450.1056514.1578694747821.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Jeudi 9 Janvier 2020 18:57:58 > Objet: Component deprecation (was: [records] Record updates for Preview/2) > The Deprecated attribute is, effectively, Deprecated. So I'm not interested in > giving it any life support. I think there is a confusion somewhere. The JVMS has no word about deprecating the attribute Deprecated, javac 14 is using it, so i don't understand. For me, a record component is at the same level as a field or a method, by example, they all have their own sub-attributes. Given that both a field and a method are using the attribute Deprecated, it is logical that a record component also uses the attribute Deprecated. The current state seems like a gratuitous discrepancy. So perhaps in the future, we may deprecate the attribute Deprecated but that's another story, for another time. > However, it is reasonable to allow the @Deprecated annotation on record > components. And, you can apply it there now! And if you do, it will get > propagated to fields, accessors, and constructor parameters, and reflection > will reflect the right thing -- because its an annotation. yes, but it will not be stored as a runtime visible annotation on a record component in the bytecode. > It is reasonable to add RECORD_COMPONENT to the target type, though not > necessary. This would cause it to be propagated through to the Record > attribute, where reflection would report it. I'm pretty not-interested in > spending any spec or compiler effort on this, though. Its an annotation; we > support annotations. It's necessary Foo.class.getRecordComponents()[0].getDeclaredAnnotations() should report if the first component is deprecated or not. R?mi > On 1/9/2020 12:51 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 9 Janvier 2020 16:07:50 >>> Objet: Re: [records] Record updates for Preview/2 >>>> - @Deprecated on record components >>> Please outline what you think we want on a separate thread? >> Currently you can not deprecate a record component unlike in Scala or in Kotlin. >> We can either allow @Deprecated or pretend that people will never make mistake, >> change their mind, specification will never change, etc >> How to fix the issue, >> - as Dmitry Bessonov said on the amber mailing list, the Deprecated annotation >> should list ElementType.RECORD_COMPONENT as possible target. >> - the JVMS should be changed to allow the attribute Deprecated on >> record_component. >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 10 22:29:23 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 10 Jan 2020 17:29:23 -0500 Subject: Component deprecation In-Reply-To: <1739217450.1056514.1578694747821.JavaMail.zimbra@u-pem.fr> References: <345654259.17331.1578531020214.JavaMail.zimbra@u-pem.fr> <1502565249.486222.1578592298680.JavaMail.zimbra@u-pem.fr> <1739217450.1056514.1578694747821.JavaMail.zimbra@u-pem.fr> Message-ID: <4aedf745-365b-fa85-0838-52714d40b84e@oracle.com> > The Deprecated attribute is, effectively, Deprecated.? So I'm not > interested in giving it any life support. > > > I think there is a confusion somewhere. > The JVMS has no word about deprecating the attribute Deprecated, javac > 14 is using it, so i don't understand. > > For me, a record component is at the same level as a field or a > method, by example, they all have their own sub-attributes. > Given that both a field and a method are using the attribute > Deprecated, it is logical that a record component also uses the > attribute Deprecated. > The current state seems like a gratuitous discrepancy. > > So perhaps in the future, we may deprecate the attribute Deprecated > but that's another story, for another time. Nope.? We have zero interest in keeping this attribute on life support.?? (And my remaining attention budget for this entire topic is about 5m, so it's mostly used just writing this mail.) > > > However, it is reasonable to allow the @Deprecated annotation on > record components.? And, you can apply it there now!? And if you > do, it will get propagated to fields, accessors, and constructor > parameters, and reflection will reflect the right thing -- because > its an annotation. > > > yes, but it will not be stored as a runtime visible annotation on a > record component in the bytecode. WHy not?? It is a runtime-visible annotation: @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.MODULE, ElementType.PARAMETER, ElementType.TYPE}) public @interface Deprecated { -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Sun Jan 12 21:16:53 2020 From: john.r.rose at oracle.com (John Rose) Date: Sun, 12 Jan 2020 13:16:53 -0800 Subject: [records] updates for Preview 6. Default access modes In-Reply-To: References: <68C1A0BD-2BE1-4840-BF1D-9EA9D76716D7@oracle.com> <6781BBDF-BFB0-4994-9247-46D50CAED2C8@oracle.com> Message-ID: <0091EE6A-A62B-42D0-9527-12670DC1631B@oracle.com> On Jan 10, 2020, at 2:04 AM, Tagir Valeev wrote: > > I don't like B. The problem with B is that having a public default you > may mistakenly expose an implementation detail, and it could be hard > to fix this when clients start to use it. With interface methods this > is less important, because if you have an implementation inside > interface, you should explicitly mark it with either 'default' > (meaning that it's still part of an interface) or 'private' (meaning > that it's an implementation detail). But specifying an interface > method with a body without a modifier at all is an error, so you will > have to think. That?s true about methods but not about classes. Like it or not (and as Brian says we need to avoid the temptation to fix past sins), if you define a nested class without modifiers, it is public. And the same is true of fields. So your point is partly valid (default *on a method* makes it clear something is not just package-scope) but I think you are working too hard to avoid my main point: Interfaces have the bad feature you say we want to avoid, which is that (many) declarations that look like package-scoped are silently promoted to public. Besides that, ?default? doesn?t convey ?public?. In all cases with members of interfaces, we have schooled ourselves to watch for the absence of the modifiers to denote ?public? not ?package? as in other classes. The presence of ?default? helps with this, but does not dispense us from the need to be basically watchful in interface bodies. (I do want to fix this, as I?ve mentioned. But it?s a separate change request.) Given that reality, and given the fact that we are not going to ?fix? that ?sin? of interfaces, I think it is reasonable to propose that we *benefit* from the design of interfaces in this case, by transferring the rules to records as well. > Records are not of this kind. My point here (which I see is not gaining a lot of ground with you and Brian, but it?s a point) is that records and interfaces are of a common kind, a restricted type whose semantics is required to be (largely) public. An interface is a public product of (mainly abstract) behaviors (plus optional defaults), while a record is a public product of stateless data components. I claim it is perfectly reasonable to view them as ?of the same kind? and in particular to reuse whatever rules we have for interfaces for records, rather than (option C or D) make up new rules. In fact, although Brian mentioned that proposal B seems to be fixing sins of the past (a siren song) it?s exactly the opposite case here: Proposal B embraces the sins of the past (interface access defaults to public), while the favored proposals C and D try to tinker with defaults in new ways, avoiding badly set defaults from the past. Which is fixing sins? If we avoid the siren song of making a new set of (probably wrong) defaults for records, and embrace the existing rules (imperfect as they are) then it?s a choice of A or B. And of those two choices, I think B is more natural, given the similarities of interface and records (semantically restricted mostly-public types). Another advantage of embracing the interface rules is that, if we want to tweak the rules later on (say, adding a package-private or non-public modifier, or something like that) the cost of changing the rules will lead to a larger benefit, for both interfaces and records (and any other similar constructs that may come along). If we do something bespoke for records, different from classes and interfaces, the risk is that we?ll be unable to justify later fixes to the access rules, since they will have lower benefit, applying to smaller proportions of the language. Keeping things uniform between records and interfaces (or for that matter records and classes) means that we can hope to make things more uniformly better later with features like ?non-public?/ ?package?/?package-private?. So, FTR, my order of preference is B, C/D, A. ? John From john.r.rose at oracle.com Mon Jan 13 23:44:35 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 13 Jan 2020 15:44:35 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> Message-ID: <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> On Jan 10, 2020, at 1:20 PM, Brian Goetz wrote: > > No objection to further hashing the specification of the hash. Care to post a proposed patch for the spec in j.l.Record? Sure: http://cr.openjdk.java.net/~jrose/draft/record-contract/ This fences out user assumptions about hashCode. While I was at it I also did equals and toString. The toString language is an example of applying similar limitations. Probably it goes into too much detail; I added it for the sake of discussion. BTW, when designing the toString methods for MethodHandle and MethodType we were ruthless about removing package prefixes, and I?m glad we did it that way. Package prefixes are noisy in toString output. I?m glad to see it going that way with Record::toString also. ? John From brian.goetz at oracle.com Mon Jan 13 23:47:59 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 13 Jan 2020 18:47:59 -0500 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> Message-ID: <1c71089f-19ee-6750-9fd7-26f431e97502@oracle.com> Overall this is good -- though I am not quite sure what the last bit means here, about "derived from the behavior of Object::equals"? 122 * The precise algorithm used in the implicitly provided implementation 123 * is unspecified and is subject to change, 124 * within the limits of the contract of {@code Object.hashCode} 125 * as derived from the behavior of {@code Object.equals}. On 1/13/2020 6:44 PM, John Rose wrote: > On Jan 10, 2020, at 1:20 PM, Brian Goetz wrote: >> No objection to further hashing the specification of the hash. Care to post a proposed patch for the spec in j.l.Record? > Sure: > > http://cr.openjdk.java.net/~jrose/draft/record-contract/ > > This fences out user assumptions about hashCode. > While I was at it I also did equals and toString. > > The toString language is an example of applying similar > limitations. Probably it goes into too much detail; I added > it for the sake of discussion. > > BTW, when designing the toString methods for MethodHandle > and MethodType we were ruthless about removing package > prefixes, and I?m glad we did it that way. Package prefixes > are noisy in toString output. I?m glad to see it going that > way with Record::toString also. > > ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Mon Jan 13 23:51:38 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 13 Jan 2020 15:51:38 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> Message-ID: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> P.S. Another way to deal with toString is just to specify exactly what we do (it?s not bad!) and follow up with a ?subject to change? message. We took this tack with MethodHandle.toString: https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandle.html#toString-- > Future releases of this API may add further information to the string representation. Therefore, the present syntax should not be parsed by applications. From john.r.rose at oracle.com Tue Jan 14 00:07:16 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 13 Jan 2020 16:07:16 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <1c71089f-19ee-6750-9fd7-26f431e97502@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> <1c71089f-19ee-6750-9fd7-26f431e97502@oracle.com> Message-ID: On Jan 13, 2020, at 3:47 PM, Brian Goetz wrote: > > Overall this is good -- though I am not quite sure what the last bit means here, about "derived from the behavior of Object::equals"? > > 122 * The precise algorithm used in the implicitly provided implementation > 123 * is unspecified and is subject to change, > 124 * within the limits of the contract of {@code Object.hashCode} > 125 * as derived from the behavior of {@code Object.equals}. The contract of Object::hashCode refers to the behavior of Object::equals, and not much else. That?s what I mean by one contract being derived from the behavior of another. There?s probably a better way to say it?? The important things are that (a) Record::equals refines the contract of Object::equals by mandating component-wise comparison (and nothing else), at least in the default implementation. And (b) Record::hashCode must be compatible with Record::equals, deriving a combined hash from the values of the components. I didn?t say *hashCodes* of the components, just *hashes*, because recursively using the honest actual *hashCodes* of the components needlessly constrains the implicit implementation. In the case of primitives, and probably also in the case of other types like nested records, if you are given a record (or tree of records), you can sometimes find faster ways to hash than first hashing every single field (and subtree, if you are doing a tree). For example, in a record of four bytes, you could pick them all up at once into a 32-bit word, and then do a little mixing. For typical records, there might be an advantage to dividing the work (on primitives and other ?easy? stuff) into 64-bit words or even hardware vectors larger than 64 bits. We buy an option on such future optimizations simply by refusing to give TMI about the implicit hashCode implementation, not even the seemingly harmless assurance that it recursively calls hashCode on wrappers. ? John P.S. In all of this I haven?t said how *bad* the wrapper hashCode methods are. Oops, there I said it. -------------- next part -------------- An HTML attachment was scrubbed... URL: From kevinb at google.com Tue Jan 14 00:23:53 2020 From: kevinb at google.com (Kevin Bourrillion) Date: Mon, 13 Jan 2020 16:23:53 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> Message-ID: On Thu, Jan 9, 2020 at 8:46 PM John Rose wrote: All we need to do is promise > that a record type?s hashCode fulfills the Object contract, especially > relative to the record?s equals method. Yes! This, exactly. Thank you. (It doesn't even matter if the words in the spec technically admit the possibility it's implemented as `return 0`, because that's just a bad implementation, and specs shouldn't have the burden of saying "by the way this isn't/shouldn't be a bad implementation".) As for toString() it doesn't have the same "Object already says it all" situation, but I would question whether all the guarantees I currently see in the patch are really necessary. How do users benefit from those guarantees? I do like the "equal values -> equal strings" one (perhaps noting that the inverse doesn't quite hold?). $.02 -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Tue Jan 14 06:05:00 2020 From: john.r.rose at oracle.com (John Rose) Date: Mon, 13 Jan 2020 22:05:00 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: References: <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> Message-ID: On Jan 13, 2020, at 4:23 PM, Kevin Bourrillion wrote: > > On Thu, Jan 9, 2020 at 8:46 PM John Rose > wrote: > > All we need to do is promise > that a record type?s hashCode fulfills the Object contract, especially > relative to the record?s equals method. > > Yes! This, exactly. Thank you. You are welcome! > (It doesn't even matter if the words in the spec technically admit the possibility it's implemented as `return 0`, because that's just a bad implementation, and specs shouldn't have the burden of saying "by the way this isn't/shouldn't be a bad implementation?.) This is a good thing to remember when writing javadoc. It gives us permission to write less, or push information down into the ?notes?. > As for toString() it doesn't have the same "Object already says it all" situation, but I would question whether all the guarantees I currently see in the patch are really necessary. How do users benefit from those guarantees? I do like the "equal values -> equal strings" one (perhaps noting that the inverse doesn't quite hold?). OK, I updated the toString bit to say less following your suggestion: + * The precise format produced by this implicitly provided implementation + * is unspecified and is subject to change. + * It will, however, always be the case that equal records will + * report equal strings, under the reasonable assumption that the + * corresponding property holds for the component types. How and if the component names and values appear is left unstated, as a matter of QoI. In some cases, a better QoI might lead to *less* string output, as when a record has a rarely set optional value, and there?s little value to including ?, optval=Optional.empty? in most of the strings. In some cases, a better QoI might lead to *more* string output, as when a record has an array field and (somehow) it is determined that the displayed value should use Arrays.toString instead of Object.toString. Dunno how to turn on and off such tweaks, but maybe we figure that out later, and in that case fewer promises about Record::toString makes for more choices in the future. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From Daniel_Heidinga at ca.ibm.com Tue Jan 14 14:49:23 2020 From: Daniel_Heidinga at ca.ibm.com (Daniel Heidinga) Date: Tue, 14 Jan 2020 14:49:23 +0000 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com>, <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> Message-ID: > >P.S. Another way to deal with toString is just to specify exactly >what we do (it?s not bad!) and follow up with a ?subject to change? >message. We took this tack with MethodHandle.toString: > >https://urldefense.proofpoint.com/v2/url?u=https-3A__docs.oracle.com_ >javase_8_docs_api_java_lang_invoke_MethodHandle.html-23toString-2D-2D >&d=DwIFaQ&c=jf_iaSHvJObTbx-siA1ZOg&r=LBQnmyrHQkEBElM8bAxhzfwLG2HbsYDd >zEznFrQoob4&m=etTTBS9y5oovHJplsJwMyjctXyoPzqEdXAMgwJPDZqw&s=In8pAaJxE >eJnblz_VUBj_JhiMndhS6hnbEK6rhceNdU&e= >> Future releases of this API may add further information to the >string representation. Therefore, the present syntax should not be >parsed by applications. > This gets a +1 from me. The format is clear - explained so it's easy to understand what the parts mean - and explicit that it may change. We can't ever stop users from parsing #toString but this is explicit that's a bad idea for them to do so. --Dan From john.r.rose at oracle.com Tue Jan 14 22:49:54 2020 From: john.r.rose at oracle.com (John Rose) Date: Tue, 14 Jan 2020 14:49:54 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> Message-ID: <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> On Jan 14, 2020, at 6:49 AM, Daniel Heidinga wrote: > > This gets a +1 from me. The format is clear - explained so it's easy > to understand what the parts mean - and explicit that it may change. > > We can't ever stop users from parsing #toString but this is explicit > that's a bad idea for them to do so. OK, so I?ve updated the proposed doc change along those lines. http://cr.openjdk.java.net/~jrose/draft/record-contract (Previous versions are ?/record-contract.{00,01}.) I adapted the javadoc from AbstractMap::toString, and then added some sample code, just for grins. It would be fine to delete the sample code. * @implNote * The implicitly provided implementation returns a string which begins * with the unqualified name of the record class and is immediately * followed by a list of components, in declaration order, * enclosed in square brackets ({@code "[]"}). Adjacent components * are separated by the characters {@code ", "} (comma and space). * Each component is rendered as the name followed by an equals sign * ({@code "="}) followed by a string representing the associated value. The language is intentionally vague about what is ?a string representing the associated value?. There are comments in the prototype that mention maybe adding quotes to some values (Strings). So I left that open. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Jan 14 23:00:44 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 15 Jan 2020 00:00:44 +0100 (CET) Subject: [records] updates for Preview 9. hashCode In-Reply-To: <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> Message-ID: <342885299.1329242.1579042844517.JavaMail.zimbra@u-pem.fr> > De: "John Rose" > ?: "Daniel Heidinga" > Cc: "amber-spec-experts" > Envoy?: Mardi 14 Janvier 2020 23:49:54 > Objet: Re: [records] updates for Preview 9. hashCode > On Jan 14, 2020, at 6:49 AM, Daniel Heidinga < [ > mailto:Daniel_Heidinga at ca.ibm.com | Daniel_Heidinga at ca.ibm.com ] > wrote: >> This gets a +1 from me. The format is clear - explained so it's easy >> to understand what the parts mean - and explicit that it may change. >> We can't ever stop users from parsing #toString but this is explicit >> that's a bad idea for them to do so. > OK, so I?ve updated the proposed doc change along those lines. > [ http://cr.openjdk.java.net/~jrose/draft/record-contract | > http://cr.openjdk.java.net/~jrose/draft/record-contract ] > (Previous versions are ?/record-contract.{00,01}.) > I adapted the javadoc from AbstractMap::toString, and > then added some sample code, just for grins. It would be > fine to delete the sample code. > * @implNote > * The implicitly provided implementation returns a string which begins > * with the unqualified name of the record class and is immediately > * followed by a list of components, in declaration order, > * enclosed in square brackets ({@code "[]"}). Adjacent components > * are separated by the characters {@code ", "} (comma and space). > * Each component is rendered as the name followed by an equals sign > * ({@code "="}) followed by a string representing the associated value. > The language is intentionally vague about what is ?a string representing > the associated value?. There are comments in the prototype that mention > maybe adding quotes to some values (Strings). So I left that open. This is almost OT but it's a question I was asked for twice, what is the rational to use the angle bracket instead of the parenthesis ? Using parenthesis makes the syntax and the returns of toString() more close together, the one mirroring the other. My answer is that a record is a named tuple, so the closest approximation we have is a list of entries (Map.Entry) hence the syntax. > ? John R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jan 14 23:05:59 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 14 Jan 2020 18:05:59 -0500 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <342885299.1329242.1579042844517.JavaMail.zimbra@u-pem.fr> References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> <342885299.1329242.1579042844517.JavaMail.zimbra@u-pem.fr> Message-ID: > > This is almost OT but it's a question I was asked for twice, > what is the rational to use the angle bracket instead of the parenthesis ? I surveyed a variety of implementations of toString(), and picked what seemed most common, which was some variant of: ??? Type[v=vVal, u=uVal] Which was good enough.? Yes, we could bikeshed it, but I'm not particularly seeing a problem that needs to be fixed, and it doens't seem obvious to me that highlighting the record-ness of it is important at? toString-time?? It's the state that matters. -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Tue Jan 14 23:35:22 2020 From: john.r.rose at oracle.com (John Rose) Date: Tue, 14 Jan 2020 15:35:22 -0800 Subject: [records] updates for Preview 9. hashCode In-Reply-To: References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> <342885299.1329242.1579042844517.JavaMail.zimbra@u-pem.fr> Message-ID: <3870BF9D-39AF-4FCE-A943-EF9CA579F898@oracle.com> On Jan 14, 2020, at 3:05 PM, Brian Goetz wrote: > > >> >> This is almost OT but it's a question I was asked for twice, >> what is the rational to use the angle bracket instead of the parenthesis ? > > I surveyed a variety of implementations of toString(), and picked what seemed most common, which was some variant of: > > Type[v=vVal, u=uVal] > > Which was good enough. Yes, we could bikeshed it, but I'm not particularly seeing a problem that needs to be fixed, and it doens't seem obvious to me that highlighting the record-ness of it is important at toString-time? It's the state that matters. In practice record-ness will be clear at a glance from the class name. I see no downsides to the existing syntax and am (like Brian) uninterested in exploring alternatives to this particular punctuation polarity. ? John P.S. ?OK, in the interests of defending this position FTR, here?s some additional rationale. This is not intended as an invitation to explore other designs which might have better rationales! A record is, first of all, a tuple, with an immediate proviso that it?s nominal, having a named type and named components. As a tuple it?s sequential and heterogeneous. As a nominal entity its components are keyed, allowing strongly type access. It is not indexed like a homogeneous entity. Lists, arrays, and sets print with square brackets, and records like them are sequential data structures (though heterogeneous), so they too print with square brackets. Unlike lists and arrays, the nominal information is added as decorations. The decorations follow the precedent of (Abstract)Map, but the similarity to Map is not close enough to mandate closer similarly to Map syntax. In particular, Maps are not (usually) ordered, and cannot be interpreted (usually) without reference to keys. Records unlike maps are always ordered and can be interpreted as pure tuples (as well as maps). Record: heterogeneous, sequential, keyed, N[a=x,b=y]. tuple*: heterogeneous, sequential, indexed, (x,y). List, array: homogeneous, sequential, indexed, [x,y]. Set: homogeneous, non-sequential, self-keyed, [x,y]. Map: homogeneous, non-sequential, keyed, {a=x,b=y}. See also: MethodHandle: MethodHandle(A)T MethodType: (A)T (*) hypothetical in Java, math notation cited. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Jan 15 01:02:11 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 15 Jan 2020 02:02:11 +0100 (CET) Subject: [records] updates for Preview 9. hashCode In-Reply-To: <3870BF9D-39AF-4FCE-A943-EF9CA579F898@oracle.com> References: <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> <342885299.1329242.1579042844517.JavaMail.zimbra@u-pem.fr> <3870BF9D-39AF-4FCE-A943-EF9CA579F898@oracle.com> Message-ID: <38304909.1336408.1579050131302.JavaMail.zimbra@u-pem.fr> > De: "John Rose" > ?: "Brian Goetz" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Mercredi 15 Janvier 2020 00:35:22 > Objet: Re: [records] updates for Preview 9. hashCode > On Jan 14, 2020, at 3:05 PM, Brian Goetz < [ mailto:brian.goetz at oracle.com | > brian.goetz at oracle.com ] > wrote: >>> This is almost OT but it's a question I was asked for twice, >>> what is the rational to use the angle bracket instead of the parenthesis ? >> I surveyed a variety of implementations of toString(), and picked what seemed >> most common, which was some variant of: >> Type[v=vVal, u=uVal] >> Which was good enough. Yes, we could bikeshed it, but I'm not particularly >> seeing a problem that needs to be fixed, and it doens't seem obvious to me that >> highlighting the record-ness of it is important at toString-time? It's the >> state that matters. > In practice record-ness will be clear at a glance from the class name. > I see no downsides to the existing syntax and am (like Brian) uninterested > in exploring alternatives to this particular punctuation polarity. just for the sake of being exhaustive, the downsides are - printing any combinations of list and records is hard to parse record Book(String author, String title) {} record Library(List books) { Library { books = List.copyOf(books); }} var libraries = List.of(new Library(List.of(books1, books2)), ...) libraries.toString() return [Libary[books=[Book[author=..., title=...], Book[author=..., title=...]]], ...] - as John said below the mathematical syntax for tuples is parenthesis not angle brackets. And i'm not advocating for any changes, it's just intellectual honesty. > ? John R?mi > P.S. ?OK, in the interests of defending this position FTR, here?s > some additional rationale. This is not intended as an invitation to > explore other designs which might have better rationales! > A record is, first of all, a tuple, with an immediate proviso that it?s > nominal, having a named type and named components. As a tuple > it?s sequential and heterogeneous. As a nominal entity its components > are keyed, allowing strongly type access. It is not indexed like a > homogeneous entity. > Lists, arrays, and sets print with square brackets, and records like them > are sequential data structures (though heterogeneous), so they too print > with square brackets. > Unlike lists and arrays, the nominal information is added as decorations. > The decorations follow the precedent of (Abstract)Map, but the > similarity to Map is not close enough to mandate closer similarly > to Map syntax. In particular, Maps are not (usually) ordered, > and cannot be interpreted (usually) without reference to keys. > Records unlike maps are always ordered and can be interpreted > as pure tuples (as well as maps). > Record: heterogeneous, sequential, keyed, N[a=x,b=y]. > tuple*: heterogeneous, sequential, indexed, (x,y). > List, array: homogeneous, sequential, indexed, [x,y]. > Set: homogeneous, non-sequential, self-keyed, [x,y]. > Map: homogeneous, non-sequential, keyed, {a=x,b=y}. > See also: > MethodHandle: MethodHandle(A)T > MethodType: (A)T > (*) hypothetical in Java, math notation cited. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Daniel_Heidinga at ca.ibm.com Wed Jan 15 03:52:58 2020 From: Daniel_Heidinga at ca.ibm.com (Daniel Heidinga) Date: Wed, 15 Jan 2020 03:52:58 +0000 Subject: [records] updates for Preview 9. hashCode In-Reply-To: <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com> References: <89F36723-E754-4378-9CDB-09D6C18853AF@oracle.com>, <2A8174A4-25CB-4F09-B760-DF778F41A387@oracle.com> <8ACEA880-D03C-4B75-874D-13C353E79102@oracle.com> <8d08ab5d-660f-4192-6f16-c7e6b9edc0f9@oracle.com> <4BA61FD6-881C-4B22-9168-EED2A4D58FB1@oracle.com> Message-ID: > >OK, so I?ve updated the proposed doc change along those lines. > > http://cr.openjdk.java.net/~jrose/draft/record-contract > >(Previous versions are ?/record-contract.{00,01}.) > >I adapted the javadoc from AbstractMap::toString, and >then added some sample code, just for grins. It would be >fine to delete the sample code. > > * @implNote > * The implicitly provided implementation returns a string which >begins > * with the unqualified name of the record class and is >immediately > * followed by a list of components, in declaration order, > * enclosed in square brackets ({@code "[]"}). Adjacent >components > * are separated by the characters {@code ", "} (comma and >space). > * Each component is rendered as the name followed by an equals >sign > * ({@code "="}) followed by a string representing the associated >value. > >The language is intentionally vague about what is ?a string >representing >the associated value?. There are comments in the prototype that >mention >maybe adding quotes to some values (Strings). So I left that open. > >? John Thanks John. This looks good to me. From gavin.bierman at oracle.com Thu Jan 16 15:39:55 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 16 Jan 2020 15:39:55 +0000 Subject: [records] New draft spec for JEP 359 In-Reply-To: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> Message-ID: <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> Dear spec-experts, I need to make a clarification regarding the earlier email. Since we are so close to entering RDP2, it wasn?t possible to get all these changes to the spec (and their corresponding compiler patches) into JDK 14. They will have to wait until the second preview of records in JDK 15; along with whatever we decide regarding the issues that Brian has raised in a recent email: https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html The records spec for JDK 14 that was approved by the CSR (JDK-8236189) is available at: http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html It contains only two of the items listed in my earlier email, viz * Forbids capture of local variables in a local record * Adding missing annotation target PARAMETER (this was already implemented in the compiler, so just a typo in the spec) Just to repeat, the others - along with aligning the use of variable arity in the record component header and the canonical constructor (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html) - will be dealt with in the second preview of Records in JDK 15. Thanks, Gavin > On 10 Jan 2020, at 13:31, Gavin Bierman wrote: > > Happy new year everyone! An updated version of the records spec is available at: > > http://cr.openjdk.java.net/~gbierman/jep359/latest > > (which currently points to http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) > > This addresses: > > * Removes `final` modifier on record components > * Adding missing annotation target PARAMETER > * Corrects discussion of @SafeVarargs annotation > * Forbids capture of local variables in a local record > * Clarifies implicit formal parameter list in a compact constructor > > Gavin > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Jan 17 06:32:49 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 17 Jan 2020 13:32:49 +0700 Subject: [records] New draft spec for JEP 359 In-Reply-To: <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> Message-ID: So it looks like the `final` modifier on record components is allowed in 14-preview? With best regards, Tagir Valeev. On Thu, Jan 16, 2020 at 10:41 PM Gavin Bierman wrote: > > Dear spec-experts, > > I need to make a clarification regarding the earlier email. Since we are so close to entering RDP2, it wasn?t possible to get all these changes to the spec (and their corresponding compiler patches) into JDK 14. They will have to wait until the second preview of records in JDK 15; along with whatever we decide regarding the issues that Brian has raised in a recent email: https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html > > The records spec for JDK 14 that was approved by the CSR (JDK-8236189) is available at: > > http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html > > It contains only two of the items listed in my earlier email, viz > > * Forbids capture of local variables in a local record > * Adding missing annotation target PARAMETER (this was already implemented in the compiler, so just a typo in the spec) > > Just to repeat, the others - along with aligning the use of variable arity in the record component header and the canonical constructor (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html) - will be dealt with in the second preview of Records in JDK 15. > > Thanks, > Gavin > > On 10 Jan 2020, at 13:31, Gavin Bierman wrote: > > Happy new year everyone! An updated version of the records spec is available at: > > http://cr.openjdk.java.net/~gbierman/jep359/latest > > (which currently points to http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) > > This addresses: > > * Removes `final` modifier on record components > * Adding missing annotation target PARAMETER > * Corrects discussion of @SafeVarargs annotation > * Forbids capture of local variables in a local record > * Clarifies implicit formal parameter list in a compact constructor > > Gavin > > > > From forax at univ-mlv.fr Fri Jan 17 09:37:01 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 17 Jan 2020 10:37:01 +0100 (CET) Subject: [records] New draft spec for JEP 359 In-Reply-To: References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> Message-ID: <1562579060.786338.1579253821654.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Tagir Valeev" > ?: "Gavin Bierman" > Cc: "amber-spec-experts" > Envoy?: Vendredi 17 Janvier 2020 07:32:49 > Objet: Re: [records] New draft spec for JEP 359 > So it looks like the `final` modifier on record components is allowed > in 14-preview? javac using the latest beta of jdk 14 rejects them. so i hope the spec also reject them. > > With best regards, > Tagir Valeev. R?mi > > On Thu, Jan 16, 2020 at 10:41 PM Gavin Bierman wrote: >> >> Dear spec-experts, >> >> I need to make a clarification regarding the earlier email. Since we are so >> close to entering RDP2, it wasn?t possible to get all these changes to the spec >> (and their corresponding compiler patches) into JDK 14. They will have to wait >> until the second preview of records in JDK 15; along with whatever we decide >> regarding the issues that Brian has raised in a recent email: >> https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html >> >> The records spec for JDK 14 that was approved by the CSR (JDK-8236189) is >> available at: >> >> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html >> >> It contains only two of the items listed in my earlier email, viz >> >> * Forbids capture of local variables in a local record >> * Adding missing annotation target PARAMETER (this was already implemented in >> the compiler, so just a typo in the spec) >> >> Just to repeat, the others - along with aligning the use of variable arity in >> the record component header and the canonical constructor >> (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html) >> - will be dealt with in the second preview of Records in JDK 15. >> >> Thanks, >> Gavin >> >> On 10 Jan 2020, at 13:31, Gavin Bierman wrote: >> >> Happy new year everyone! An updated version of the records spec is available at: >> >> http://cr.openjdk.java.net/~gbierman/jep359/latest >> >> (which currently points to >> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) >> >> This addresses: >> >> * Removes `final` modifier on record components >> * Adding missing annotation target PARAMETER >> * Corrects discussion of @SafeVarargs annotation >> * Forbids capture of local variables in a local record >> * Clarifies implicit formal parameter list in a compact constructor >> >> Gavin >> >> >> From amaembo at gmail.com Fri Jan 17 11:22:09 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 17 Jan 2020 18:22:09 +0700 Subject: [records] New draft spec for JEP 359 In-Reply-To: <1562579060.786338.1579253821654.JavaMail.zimbra@u-pem.fr> References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> <1562579060.786338.1579253821654.JavaMail.zimbra@u-pem.fr> Message-ID: The spec from the previous e-mail [1] allows them, so either spec or javac should be updated (or we can ignore it, as this is a minor detail). With best regards, Tagir Valeev. [1] http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html On Fri, Jan 17, 2020 at 5:02 PM Remi Forax wrote: > > ----- Mail original ----- > > De: "Tagir Valeev" > > ?: "Gavin Bierman" > > Cc: "amber-spec-experts" > > Envoy?: Vendredi 17 Janvier 2020 07:32:49 > > Objet: Re: [records] New draft spec for JEP 359 > > > So it looks like the `final` modifier on record components is allowed > > in 14-preview? > > javac using the latest beta of jdk 14 rejects them. > so i hope the spec also reject them. > > > > > With best regards, > > Tagir Valeev. > > R?mi > > > > > On Thu, Jan 16, 2020 at 10:41 PM Gavin Bierman wrote: > >> > >> Dear spec-experts, > >> > >> I need to make a clarification regarding the earlier email. Since we are so > >> close to entering RDP2, it wasn?t possible to get all these changes to the spec > >> (and their corresponding compiler patches) into JDK 14. They will have to wait > >> until the second preview of records in JDK 15; along with whatever we decide > >> regarding the issues that Brian has raised in a recent email: > >> https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html > >> > >> The records spec for JDK 14 that was approved by the CSR (JDK-8236189) is > >> available at: > >> > >> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html > >> > >> It contains only two of the items listed in my earlier email, viz > >> > >> * Forbids capture of local variables in a local record > >> * Adding missing annotation target PARAMETER (this was already implemented in > >> the compiler, so just a typo in the spec) > >> > >> Just to repeat, the others - along with aligning the use of variable arity in > >> the record component header and the canonical constructor > >> (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html) > >> - will be dealt with in the second preview of Records in JDK 15. > >> > >> Thanks, > >> Gavin > >> > >> On 10 Jan 2020, at 13:31, Gavin Bierman wrote: > >> > >> Happy new year everyone! An updated version of the records spec is available at: > >> > >> http://cr.openjdk.java.net/~gbierman/jep359/latest > >> > >> (which currently points to > >> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) > >> > >> This addresses: > >> > >> * Removes `final` modifier on record components > >> * Adding missing annotation target PARAMETER > >> * Corrects discussion of @SafeVarargs annotation > >> * Forbids capture of local variables in a local record > >> * Clarifies implicit formal parameter list in a compact constructor > >> > >> Gavin > >> > >> > >> From gavin.bierman at oracle.com Mon Jan 20 13:43:58 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 20 Jan 2020 13:43:58 +0000 Subject: [records] New draft spec for JEP 359 In-Reply-To: References: <888E8A03-EDBE-4E07-A0C2-F15D5386AE8D@oracle.com> <6F93DB60-27EA-494E-AB6E-48BD7C6B978B@oracle.com> <1562579060.786338.1579253821654.JavaMail.zimbra@u-pem.fr> Message-ID: <91A05359-9A8E-46AC-941D-8300267F93FD@oracle.com> As I mentioned in the original email, some fixes didn?t make it through the JDK 14 process. So, on this particular issue, the compiler is correct, and the spec has a bug, which will be fixed in JDK 15. (I agree that it is a minor detail.) Gavin > On 17 Jan 2020, at 11:22, Tagir Valeev wrote: > > The spec from the previous e-mail [1] allows them, so either spec or > javac should be updated (or we can ignore it, as this is a minor > detail). > > With best regards, > Tagir Valeev. > > [1] http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html > > On Fri, Jan 17, 2020 at 5:02 PM Remi Forax wrote: >> >> ----- Mail original ----- >>> De: "Tagir Valeev" >>> ?: "Gavin Bierman" >>> Cc: "amber-spec-experts" >>> Envoy?: Vendredi 17 Janvier 2020 07:32:49 >>> Objet: Re: [records] New draft spec for JEP 359 >> >>> So it looks like the `final` modifier on record components is allowed >>> in 14-preview? >> >> javac using the latest beta of jdk 14 rejects them. >> so i hope the spec also reject them. >> >>> >>> With best regards, >>> Tagir Valeev. >> >> R?mi >> >>> >>> On Thu, Jan 16, 2020 at 10:41 PM Gavin Bierman wrote: >>>> >>>> Dear spec-experts, >>>> >>>> I need to make a clarification regarding the earlier email. Since we are so >>>> close to entering RDP2, it wasn?t possible to get all these changes to the spec >>>> (and their corresponding compiler patches) into JDK 14. They will have to wait >>>> until the second preview of records in JDK 15; along with whatever we decide >>>> regarding the issues that Brian has raised in a recent email: >>>> https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html >>>> >>>> The records spec for JDK 14 that was approved by the CSR (JDK-8236189) is >>>> available at: >>>> >>>> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200115/specs/records-jls.html >>>> >>>> It contains only two of the items listed in my earlier email, viz >>>> >>>> * Forbids capture of local variables in a local record >>>> * Adding missing annotation target PARAMETER (this was already implemented in >>>> the compiler, so just a typo in the spec) >>>> >>>> Just to repeat, the others - along with aligning the use of variable arity in >>>> the record component header and the canonical constructor >>>> (http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-December/001885.html) >>>> - will be dealt with in the second preview of Records in JDK 15. >>>> >>>> Thanks, >>>> Gavin >>>> >>>> On 10 Jan 2020, at 13:31, Gavin Bierman wrote: >>>> >>>> Happy new year everyone! An updated version of the records spec is available at: >>>> >>>> http://cr.openjdk.java.net/~gbierman/jep359/latest >>>> >>>> (which currently points to >>>> http://cr.openjdk.java.net/~gbierman/jep359/jep359-20200110/specs/records-jls.html) >>>> >>>> This addresses: >>>> >>>> * Removes `final` modifier on record components >>>> * Adding missing annotation target PARAMETER >>>> * Corrects discussion of @SafeVarargs annotation >>>> * Forbids capture of local variables in a local record >>>> * Clarifies implicit formal parameter list in a compact constructor >>>> >>>> Gavin >>>> >>>> >>>> From amaembo at gmail.com Wed Jan 22 08:03:36 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 22 Jan 2020 15:03:36 +0700 Subject: [records] Specify use of @Override and @SafeVarargs on record components Message-ID: 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. What do you think? With best regards, Tagir Valeev. [1] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.7 [2] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.4 From amaembo at gmail.com Wed Jan 22 09:54:06 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 22 Jan 2020 16:54:06 +0700 Subject: [patterns] Labeled statements Message-ID: Hello! Current spec draft 6.3.2 [1] says nothing about the interaction of pattern scopes and labeled statements, and reading spec 14.5 and 14.7 I conclude that the labeled statement is a kind of statement. E.g.: public static void main(String[] args) { Object o = "hello"; L0: if(!(o instanceof String s)) { return; } System.out.println(s.length()); } I conclude that the `if` statement has no immediately enclosing block because it's just a part of labeled statement, which is enclosed into a block. So according to the current spec draft, the `s` variable should not be resolvable after the labeled statement. Though javac implementation disagrees with me and happily accepts this code. So it looks like the spec should be updated to specify how labeled statements are handled. [1] http://cr.openjdk.java.net/~gbierman/jep305/jep305-20191021/specs/patterns-instanceof-jls.html#jls-6.3.2 From brian.goetz at oracle.com Wed Jan 22 15:35:16 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 22 Jan 2020 10:35:16 -0500 Subject: [records] Specify use of @Override and @SafeVarargs on record components In-Reply-To: References: Message-ID: <6ff3266e-0f02-f747-6106-cb9faef8c250@oracle.com> Nice catch, Tagir.? Historically the @Target meta-anno has been strong enough to keep people from putting annos in the wrong place, but with the "clever" handling of annos with records (accepting any of the target types an anno can be reasonably pushed down to), we don't have any way of saying "except these, which make no sense." I agree it is silly to put these annos on record components. On the other hand, I am not sure the spec should prohibit them -- seems more the sort of thing an IDE should say "hey, that was probably dumb, are you sure you want that?" In any case, it is reasonable for a compiler to error in the case of @Override on a component whose accessor does not override something -- because we should type-check implicit members as well as explicit. On 1/22/2020 3:03 AM, 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. > What do you think? > > With best regards, > Tagir Valeev. > > [1] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.7 > [2] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.4 -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 22 15:37:23 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 22 Jan 2020 10:37:23 -0500 Subject: [patterns] Labeled statements In-Reply-To: References: Message-ID: <585e668c-2d23-26f0-8d6a-e78a2fa5e790@oracle.com> Thanks, you are right.? Labeled statements are a kind of statement, which is a clever way to spec it (and the nonlocal control flow they support), but the intent was never for labeled statements to create their own scopes.? For purposes of flow scoping, labels are not relevant, but obviously that's not what the spec says, and this needs should be clarified. On 1/22/2020 4:54 AM, Tagir Valeev wrote: > Hello! > > Current spec draft 6.3.2 [1] says nothing about the interaction of > pattern scopes and labeled statements, and reading spec 14.5 and 14.7 > I conclude that the labeled statement is a kind of statement. > > E.g.: > > public static void main(String[] args) { > Object o = "hello"; > L0: if(!(o instanceof String s)) { > return; > } > System.out.println(s.length()); > } > > I conclude that the `if` statement has no immediately enclosing block > because it's just a part of labeled statement, which is enclosed into > a block. So according to the current spec draft, the `s` variable > should not be resolvable after the labeled statement. Though javac > implementation disagrees with me and happily accepts this code. > > So it looks like the spec should be updated to specify how labeled > statements are handled. > > [1] http://cr.openjdk.java.net/~gbierman/jep305/jep305-20191021/specs/patterns-instanceof-jls.html#jls-6.3.2 -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Wed Jan 22 16:18:35 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 22 Jan 2020 23:18:35 +0700 Subject: [records] Specify use of @Override and @SafeVarargs on record components In-Reply-To: <6ff3266e-0f02-f747-6106-cb9faef8c250@oracle.com> References: <6ff3266e-0f02-f747-6106-cb9faef8c250@oracle.com> Message-ID: I agree it is silly to put these annos on record components. On the other > hand, I am not sure the spec should prohibit them -- seems more the sort of > thing an IDE should say "hey, that was probably dumb, are you sure you want > that?" > > In any case, it is reasonable for a compiler to error in the case of > @Override on a component whose accessor does not override something -- > because we should type-check implicit members as well as explicit. > The same argument applies to @SafeVarargs. The compiler will implicitly apply it to the accessor method but according to the spec, this annotation cannot be applied to non-vararg method, and accessor method is never vararg, hence this should be an error. I feel this should be covered by spec because @SafeVarargs is fully specified in any other aspect. E. g. it's equally silly to put it on the method without arguments and it's a compilation error, not just IDE warning. With best regards, Tagir Valeev. > > > > On 1/22/2020 3:03 AM, 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. > > What do you think? > > With best regards, > Tagir Valeev. > > [1] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.7 > [2] https://docs.oracle.com/javase/specs/jls/se13/html/jls-9.html#jls-9.6.4.4 > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amalloy at google.com Wed Jan 22 18:19:03 2020 From: amalloy at google.com (Alan Malloy) Date: Wed, 22 Jan 2020 10:19:03 -0800 Subject: More constraints on nesting that could be relaxed Message-ID: The recent thread about "cleaner nesting" reminded me of a related language constraint that has caused me a bit of a headache recently, and I wonder if we can clean it up at the same time. Somewhat recently, interfaces have gained the ability to have private methods, for use as helpers in implementing default methods. However, there is currently no plan for private fields or private nested classes to be allowed in those contexts, even private static final fields. I don't see any particularly compelling reason for this: private fields made no sense for interfaces in Java 1.0, but since interfaces can now have private behavior, it makes sense for them to have private state to support that behavior, or at the very least private constants. For example, it seems like it should be totally fine to write: interface Actor { private static final Logger logger = new Logger(); default void act() { logger.log("act not implemented"); } } But this is illegal because logger is not permitted to have the private modifier. Instead, you have to either make the logger public (polluting the namespace of inheriting classes and breaking encapsulation of your interface), or else invent some public class that's allowed to hold a private method (still polluting the namespace, but keeping the field hidden): interface Actor { /** Do not use. */ public static final class Private { private static final Logger logger = new Logger(); } default void act() { Private.logger.log("act not implemented"); } } Is this something we could include in the overall nesting reform plan? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 22 18:52:05 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 22 Jan 2020 13:52:05 -0500 Subject: More constraints on nesting that could be relaxed In-Reply-To: References: Message-ID: Understood; restricting to methods only here was probably gratuitously bespoke.? I would indeed like to clean this up too (though, while it might sound like a "one line change to the compiler", it is actually way more work than it sounds, because it affects both JLS and JVMS, so requires code changes not only in javac but in the runtime, along with JCK and spec changes for both language and VM.) (On the other hand, the less narrowly-targeted the set of changes are, the more it is to become an attractive nuisance, where people seek to characterize their favorite bit of non-orthogonality or chafing restriction as a "better nesting" proposal, in the hopes of jumping on the bus.) The main question is how.? It seems justifiable for the Records JEP to expand somewhat beyond the boundary of record-specific functionality so that it can "complete" rows or columns on the feature matrix; by this, it is reasonable to address the static-in-inner restrictions (because that impinges on nested records) and local enums/interfaces (because these are row-completing moves.)? On the other hand, trying to cram local methods in with records is clearly way over the line, and things like this are somewhere near the line and could probably be pulled in (this impinges on "private records in interfaces.") If they were impractical to do as part of the remaining records effort (either because it is too far from the design center of records, or because we don't want to burden the records timeline with this), it is not obvious what the best alternative delivery vehicle is.? Seems too small for a JEP on its own; we don't do language features without JEPs; and while a general "better nesting" JEP is possible, as per the above "other hand" comment it is likely to be a sort of attractive nuisance. On 1/22/2020 1:19 PM, Alan Malloy wrote: > The recent thread about "cleaner nesting" reminded me of a related > language constraint that has caused me a bit of a headache recently, > and I wonder if we can clean it up at the same time. > > Somewhat recently, interfaces have gained the ability to have private > methods, for use as helpers in implementing default methods. > However,?there is currently no plan for private fields or private > nested classes to be allowed in those contexts, even private static > final fields. I don't see any particularly compelling reason for this: > private fields made no sense for interfaces in Java 1.0, but since > interfaces can now have private behavior, it makes sense for them to > have private state to support that behavior, or at the very least > private constants. > > For example, it seems like it should be totally fine to write: > > interface Actor { > ? private static final Logger logger = new Logger(); > ? default void act() { > ? ? logger.log("act not implemented"); > ? } > } > > But this is illegal because logger is not permitted to have the > private modifier. Instead, you have to either make the logger public > (polluting the namespace of inheriting classes and breaking > encapsulation of your interface), or else invent some public class > that's allowed to hold a private method (still polluting the > namespace, but keeping the field hidden): > > interface Actor { > ? /** Do not use. */ > ? public static final class Private { > ? ? private static final Logger logger = new Logger(); > ? } > ? default void act() { > ? ? Private.logger.log("act not implemented"); > ? } > } > > Is this something we could include in the overall nesting reform plan? -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Jan 25 03:33:07 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 25 Jan 2020 10:33:07 +0700 Subject: IntelliJ IDEA 2020.1 EAP with Records/Patterns support Message-ID: Hello! I'm happy to announce that JetBrains started an Early Access Program for IntelliJ IDEA 2020.1. You can download the EAP builds here (fully functional for 30 days, no registration/payment required): https://www.jetbrains.com/idea/nextversion/ Here's release notes blog post: https://blog.jetbrains.com/idea/2020/01/intellij-idea-2020-1-eap/ In particular, these builds already have decent support for Java 14 Preview features, including records and patterns in instanceof. There are still many known problems (most notable with formatting and refactorings), but all the basic functionality (syntax highlighting, error highlighting, code completion, symbol resolution, navigation, etc.) already works. Also, for patterns, we have an inspection that suggests replacing the instanceof+subsequent variable declaration and cast sequence with instanceof + pattern. Feel free to try this build. If you have any feedback you can contact me or post new issues to https://youtrack.jetbrains.com. With best regards, Tagir Valeev. From brian.goetz at oracle.com Sat Jan 25 17:53:59 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 25 Jan 2020 12:53:59 -0500 Subject: IntelliJ IDEA 2020.1 EAP with Records/Patterns support In-Reply-To: References: Message-ID: Great news, Tagir.? It is super to see strong early IDE support -- that means we will are more likely to get useful feedback when we can still act on it! On 1/24/2020 10:33 PM, Tagir Valeev wrote: > Hello! > > I'm happy to announce that JetBrains started an Early Access Program > for IntelliJ IDEA 2020.1. You can download the EAP builds here (fully > functional for 30 days, no registration/payment required): > https://www.jetbrains.com/idea/nextversion/ > > Here's release notes blog post: > https://blog.jetbrains.com/idea/2020/01/intellij-idea-2020-1-eap/ > > In particular, these builds already have decent support for Java 14 > Preview features, including records and patterns in instanceof. There > are still many known problems (most notable with formatting and > refactorings), but all the basic functionality (syntax highlighting, > error highlighting, code completion, symbol resolution, navigation, > etc.) already works. Also, for patterns, we have an inspection that > suggests replacing the instanceof+subsequent variable declaration and > cast sequence with instanceof + pattern. > > Feel free to try this build. If you have any feedback you can contact > me or post new issues to https://youtrack.jetbrains.com. > > With best regards, > Tagir Valeev. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Jan 26 21:53:31 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 26 Jan 2020 22:53:31 +0100 (CET) Subject: IntelliJ IDEA 2020.1 EAP with Records/Patterns support In-Reply-To: References: Message-ID: <1467895144.1543.1580075611141.JavaMail.zimbra@u-pem.fr> As a test, i've taken the source of Effective Java (3rd Ed) and change them to use var, switch expression, records and the instanceof with the type test pattern. [ https://github.com/forax/effective-java-3e-source-code | https://github.com/forax/effective-java-3e-source-code ] IntelliJ works great ! No problem. regards, R?mi > De: "Brian Goetz" > ?: "Tagir Valeev" , "amber-dev" , > "amber-spec-experts" > Envoy?: Samedi 25 Janvier 2020 18:53:59 > Objet: Re: IntelliJ IDEA 2020.1 EAP with Records/Patterns support > Great news, Tagir. It is super to see strong early IDE support -- that means we > will are more likely to get useful feedback when we can still act on it! > On 1/24/2020 10:33 PM, Tagir Valeev wrote: >> Hello! >> I'm happy to announce that JetBrains started an Early Access Program >> for IntelliJ IDEA 2020.1. You can download the EAP builds here (fully >> functional for 30 days, no registration/payment required): [ >> https://www.jetbrains.com/idea/nextversion/ | >> https://www.jetbrains.com/idea/nextversion/ ] Here's release notes blog post: [ >> https://blog.jetbrains.com/idea/2020/01/intellij-idea-2020-1-eap/ | >> https://blog.jetbrains.com/idea/2020/01/intellij-idea-2020-1-eap/ ] In >> particular, these builds already have decent support for Java 14 >> Preview features, including records and patterns in instanceof. There >> are still many known problems (most notable with formatting and >> refactorings), but all the basic functionality (syntax highlighting, >> error highlighting, code completion, symbol resolution, navigation, >> etc.) already works. Also, for patterns, we have an inspection that >> suggests replacing the instanceof+subsequent variable declaration and >> cast sequence with instanceof + pattern. >> Feel free to try this build. If you have any feedback you can contact >> me or post new issues to [ https://youtrack.jetbrains.com/ | >> https://youtrack.jetbrains.com ] . >> With best regards, >> Tagir Valeev. -------------- next part -------------- An HTML attachment was scrubbed... URL: From joe.darcy at oracle.com Tue Jan 28 04:00:03 2020 From: joe.darcy at oracle.com (Joe Darcy) Date: Mon, 27 Jan 2020 20:00:03 -0800 Subject: Towards cleaner nesting In-Reply-To: References: <9543b9f7-9afe-489d-bda1-50d21beca373@oracle.com> <557B23DE-2C4B-4A09-8E27-8F0EE97A7009@oracle.com> <21bddd5b-fc3b-9d72-9d35-8faf4510bb5b@oracle.com> <0F1D5DA2-6D2A-4F9B-88B8-140A2F3F0279@oracle.com> <3e03bd62-a17f-ff94-031f-ef38aa1797b7@oracle.com> <2A9C954C-ABF9-49C2-A85A-946414FF51B9@oracle.com> Message-ID: <623b98e7-a5ef-388a-7723-6950bdf267ae@oracle.com> Catching up on email... On 1/10/2020 1:43 PM, Brian Goetz wrote: [snip] >> >>>> - annotation types can't directly contain things prohibited in >>>> interfaces, or instance/local methods (maybe) >>> ? - currently annos cannot have fields, default methods, or static >>> methods -- this still seems a reasonable restriction. >> Why? I mean, I don't really care all that much, but if we're removing >> ad hoc restrictions, this one (no methods) certainly seems ad hoc. We >> don't even need new syntax. > > Because annotations are supposed to be pure metadata.? These things > all pull state and behavior into something that is supposed to be pure > metadata.?? I mean, sure, I can imagine a world that blurs the > distinction between annotations and interfaces, but I'm not seeing > these restrictions as _gratuitous_; they were made deliberately. > The design center of usage of annotations assumes the identity of Class objects can be used to distinguish different annotation types via AnnotatedElement.getAnnotation?(Class annotationClass). In a class file, an annotation type is identified by name and a runtime lookup occurs to convert the name to a class object. A known limitation of the API is the absence of a mechanism to provide a class loader to mediate this lookup (JDK-6537080). This may interact with plans to remove nested restrictions on annotation types. HTH, -Joe