From james.laskey at oracle.com Mon Aug 5 12:37:45 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 5 Aug 2019 09:37:45 -0300 Subject: Programmer's Guide To Text Blocks Message-ID: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> The enclosed PDF is the content of the proposed "Programmer's Guide To Text Blocks". Document source is located at http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.md http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html Please review and comment back to this mailing list. Cheers, -- Jim From alex.buckley at oracle.com Mon Aug 5 18:49:14 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 5 Aug 2019 11:49:14 -0700 Subject: Programmer's Guide To Text Blocks In-Reply-To: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: On 8/5/2019 5:37 AM, Jim Laskey wrote: > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html - Please number the guidelines like in the var style guidelines. - "Guideline: If a string literal fits on a single line" -- A string literal CAN ONLY fit on a single line; you mean "If a string fits ..." - "Guideline: Most text blocks should be indented to align with neighbouring Java code." should come after "Guideline: Avoid aligning the opening and closing delimiters" - "Guideline: Avoid in-line text blocks within complex expressions" -- now your readers are wondering why a normal `for` loop is "complex". You're going for something about: be cautious using text blocks in nested expressions. A `for` loop which pushes the text block down one level, into the `for` header, is an example; another example would be passing a text block in a method call (which is probably OK); another example would be passing a text block in a truly madly deeply nested expression (which isn't OK; show one). - IMO a variable declaration with a text block on the RHS cries out for a `var` on the LHS. The """ is as clear a marker as you'll ever get about the inferred type of the variable. Is there a reason to not use `var` almost everywhere here, and to explicitly recommend that? - I think there should be a guideline that says it's OK to have \n sequences in a text block -- there may be times when that's more readable overall than physically introducing a newline. - "Guideline: It is sometimes reasonable to fully left justify a wide string" -- I think it's not merely reasonable, I think it's recommended. - "... when the closing delimiter is likely to scroll out of view." -- when does this happen? There's context here which is not stated in the guidelines, especially when an earlier guideline said to put the closing delimiter on its own line. Alex From james.laskey at oracle.com Tue Aug 6 16:20:36 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Tue, 6 Aug 2019 13:20:36 -0300 Subject: Programmer's Guide To Text Blocks In-Reply-To: References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: <5ECBF4A0-FD52-454C-B8C4-BA5FAED2651D@oracle.com> Thank you. > On Aug 5, 2019, at 3:49 PM, Alex Buckley wrote: > > On 8/5/2019 5:37 AM, Jim Laskey wrote: >> http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html > > - Please number the guidelines like in the var style guidelines. > Done . > - "Guideline: If a string literal fits on a single line" -- A string literal CAN ONLY fit on a single line; you mean "If a string fits ..." Done. > > - "Guideline: Most text blocks should be indented to align with neighbouring Java code." should come after "Guideline: Avoid aligning the opening and closing delimiters" Done. > > - "Guideline: Avoid in-line text blocks within complex expressions" -- now your readers are wondering why a normal `for` loop is "complex". You're going for something about: be cautious using text blocks in nested expressions. A `for` loop which pushes the text block down one level, into the `for` header, is an example; another example would be passing a text block in a method call (which is probably OK); another example would be passing a text block in a truly madly deeply nested expression (which isn't OK; show one). // ORIGINAL String poem = new String(Files.readAllBytes(Paths.get("jabberwocky.txt"))); String middleVerses = Pattern.compile("\\n\\n") .splitAsStream(poem) .match(verse -> !""" ?Twas brillig, and the slithy toves Did gyre and gimble in the wabe; All mimsy were the borogoves, And the mome raths outgrabe. """.equals(verse)) .collect(Collectors.joining("\n\n")); // BETTER String firstLastVerse = """ ?Twas brillig, and the slithy toves Did gyre and gimble in the wabe; All mimsy were the borogoves, And the mome raths outgrabe. """; String poem = new String(Files.readAllBytes(Paths.get("jabberwocky.txt"))); String middleVerses = Pattern.compile("\\n\\n") .splitAsStream(poem) .match(verse -> !firstLastVerse.equals(verse)) .collect(Collectors.joining("\n\n")); > > - IMO a variable declaration with a text block on the RHS cries out for a `var` on the LHS. The """ is as clear a marker as you'll ever get about the inferred type of the variable. Is there a reason to not use `var` almost everywhere here, and to explicitly recommend that? I would agree except this is not a discussion about the cleverness of the "var" feature, it is about the String-y-ness of the text block feature. Repeated emphasis highlighting String will surely drive that aspect home. > > - I think there should be a guideline that says it's OK to have \n sequences in a text block -- there may be times when that's more readable overall than physically introducing a newline. (Alex provided an example.) > > - "Guideline: It is sometimes reasonable to fully left justify a wide string" -- I think it's not merely reasonable, I think it's recommended. Done. > > - "... when the closing delimiter is likely to scroll out of view." -- when does this happen? There's context here which is not stated in the guidelines, especially when an earlier guideline said to put the closing delimiter on its own line. G10. Similarly, it is also reasonable to fully left justify a text block when a high line count causes the closing delimiter is likely to vertically scroll out of view. This allows the reader to track indentation with the left margin when the closing delimiter is out of view. example > > Alex From amaembo at gmail.com Mon Aug 12 06:01:46 2019 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 12 Aug 2019 13:01:46 +0700 Subject: Programmer's Guide To Text Blocks In-Reply-To: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: Hello! In general, looks good, thanks! > *Guideline:* *Avoid aligning the opening and closing delimiters and the text block's left margin. This requires reindentation of the text block if the variable name or modifiers are changed.* I'm not sure about this recommendation. Reindentation could be necessary in other cases as well, e.g. if you move this declaration to the nested block. I think every IDE will take care of any necessary reindentation for you. It's similar to a method declaration that contains several parameters one per line. E.g. (from OpenJDK Collectors class): public static Collector joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) Here three parameters are indented in the same way: if method name, modifiers, return type or type parameters ever change, this would require the reindentation of second and third lines (and IDEs do this automatically). Is such a code style acceptable for Java? If yes, then indenting opening and closing delimiters of the text block should also be acceptable, in my opinion. With best regards, Tagir Valeev. On Mon, Aug 5, 2019 at 7:38 PM Jim Laskey wrote: > The enclosed PDF is the content of the proposed "Programmer's Guide To > Text Blocks". Document source is located at > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.md > > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html > > Please review and comment back to this mailing list. > > Cheers, > > -- Jim > > From amaembo at gmail.com Mon Aug 12 06:08:33 2019 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 12 Aug 2019 13:08:33 +0700 Subject: Programmer's Guide To Text Blocks In-Reply-To: References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: > *Guideline:* *It is sometimes reasonable to fully left justify a wide string in order to avoid horizontal scrolling or line wrapping.* If lines are too long and make reading the source file harder, I'd recommend externalizing the string instead (putting into the resource file). If this is hard to do, I would leave it as is. Horizontal scrolling is not a big problem, but left indentation breaks the flow in the common case when you actually don't need to read the text block content. Well "sometimes reasonable" looks ok to me, but I would not agree with Alex that this should be "recommended". With best regards, Tagir Valeev. On Mon, Aug 12, 2019 at 1:01 PM Tagir Valeev wrote: > Hello! > > In general, looks good, thanks! > > > *Guideline:* *Avoid aligning the opening and closing delimiters and the > text block's left margin. This requires reindentation of the text block if > the variable name or modifiers are changed.* > > I'm not sure about this recommendation. Reindentation could be necessary > in other cases as well, e.g. if you move this declaration to the nested > block. I think every IDE will take care of any necessary reindentation for > you. It's similar to a method declaration that contains several parameters > one per line. E.g. (from OpenJDK Collectors class): > > public static Collector joining(CharSequence > delimiter, > CharSequence > prefix, > CharSequence > suffix) > > Here three parameters are indented in the same way: if method name, > modifiers, return type or type parameters ever change, this would require > the reindentation of second and third lines (and IDEs do this > automatically). Is such a code style acceptable for Java? If yes, then > indenting opening and closing delimiters of the text block should also be > acceptable, in my opinion. > > With best regards, > Tagir Valeev. > > On Mon, Aug 5, 2019 at 7:38 PM Jim Laskey wrote: > >> The enclosed PDF is the content of the proposed "Programmer's Guide To >> Text Blocks". Document source is located at >> http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.md >> >> http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html >> >> Please review and comment back to this mailing list. >> >> Cheers, >> >> -- Jim >> >> From james.laskey at oracle.com Mon Aug 12 11:46:01 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 12 Aug 2019 08:46:01 -0300 Subject: Programmer's Guide To Text Blocks In-Reply-To: References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: <1CA9D086-671B-4120-9B26-69B7EB8B8536@oracle.com> Thank you, Tagir. Understand and sympathize with your comment, but it's not quite the same scenario. The example you give (sort of) requires alignment with an element (first arg) of the opening line. The point we are trying to carry is that the content "doesn't" need to (maybe shouldn't) align with the opening delimiter. That is, don't get fixated on doing so. A more relevant example is; String[] keywords = new String[] { "red", "green", "blue" }; We wouldn't expect to align on the open brace in this case. Cheers, - Jim > On Aug 12, 2019, at 3:01 AM, Tagir Valeev wrote: > > Hello! > > In general, looks good, thanks! > > > Guideline: Avoid aligning the opening and closing delimiters and the text block's left margin. This requires reindentation of the text block if the variable name or modifiers are changed. > > I'm not sure about this recommendation. Reindentation could be necessary in other cases as well, e.g. if you move this declaration to the nested block. I think every IDE will take care of any necessary reindentation for you. It's similar to a method declaration that contains several parameters one per line. E.g. (from OpenJDK Collectors class): > > public static Collector joining(CharSequence delimiter, > CharSequence prefix, > CharSequence suffix) > > Here three parameters are indented in the same way: if method name, modifiers, return type or type parameters ever change, this would require the reindentation of second and third lines (and IDEs do this automatically). Is such a code style acceptable for Java? If yes, then indenting opening and closing delimiters of the text block should also be acceptable, in my opinion. > > With best regards, > Tagir Valeev. > > On Mon, Aug 5, 2019 at 7:38 PM Jim Laskey > wrote: > The enclosed PDF is the content of the proposed "Programmer's Guide To Text Blocks". Document source is located at > > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.md > > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html > > Please review and comment back to this mailing list. > > Cheers, > > -- Jim > From james.laskey at oracle.com Mon Aug 12 11:53:08 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 12 Aug 2019 08:53:08 -0300 Subject: Programmer's Guide To Text Blocks In-Reply-To: References: <35DC79A2-2E08-4607-8558-632179A155A1@oracle.com> Message-ID: <8AE6EE11-EC4C-4231-8283-78BB7533E0CE@oracle.com> I agree, but long lines are inevitable. There are plenty of examples of this already. Pulling the text block out of line and putting in a separate statement or as a static final is a good middle ground versus externalizing. What will make these scenarios more palatable would be the introduction of a line continuation mechanism. This was discussed earlier on this this list re "\" . Cheers, -- Jim > On Aug 12, 2019, at 3:08 AM, Tagir Valeev wrote: > > > Guideline: It is sometimes reasonable to fully left justify a wide string in order to avoid horizontal scrolling or line wrapping. > > If lines are too long and make reading the source file harder, I'd recommend externalizing the string instead (putting into the resource file). If this is hard to do, I would leave it as is. Horizontal scrolling is not a big problem, but left indentation breaks the flow in the common case when you actually don't need to read the text block content. Well "sometimes reasonable" looks ok to me, but I would not agree with Alex that this should be "recommended". > > With best regards, > Tagir Valeev. > > On Mon, Aug 12, 2019 at 1:01 PM Tagir Valeev > wrote: > Hello! > > In general, looks good, thanks! > > > Guideline: Avoid aligning the opening and closing delimiters and the text block's left margin. This requires reindentation of the text block if the variable name or modifiers are changed. > > I'm not sure about this recommendation. Reindentation could be necessary in other cases as well, e.g. if you move this declaration to the nested block. I think every IDE will take care of any necessary reindentation for you. It's similar to a method declaration that contains several parameters one per line. E.g. (from OpenJDK Collectors class): > > public static Collector joining(CharSequence delimiter, > CharSequence prefix, > CharSequence suffix) > > Here three parameters are indented in the same way: if method name, modifiers, return type or type parameters ever change, this would require the reindentation of second and third lines (and IDEs do this automatically). Is such a code style acceptable for Java? If yes, then indenting opening and closing delimiters of the text block should also be acceptable, in my opinion. > > With best regards, > Tagir Valeev. > > On Mon, Aug 5, 2019 at 7:38 PM Jim Laskey > wrote: > The enclosed PDF is the content of the proposed "Programmer's Guide To Text Blocks". Document source is located at > > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.md > > http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html > > Please review and comment back to this mailing list. > > Cheers, > > -- Jim > From brian.goetz at oracle.com Mon Aug 12 13:26:18 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 12 Aug 2019 09:26:18 -0400 Subject: Comment received on amber-spec-comments Message-ID: The following comment was received on amber-spec-comments: https://mail.openjdk.java.net/pipermail/amber-spec-comments/2019-August/000028.html From james.laskey at oracle.com Tue Aug 13 12:46:13 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Tue, 13 Aug 2019 09:46:13 -0300 Subject: Escape Sequences For Managing Whitespace (Preview) Message-ID: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> https://bugs.openjdk.java.net/browse/JDK-8227870 Comment back to this list, thank you. Cheers, -- Jim From john.r.rose at oracle.com Tue Aug 13 17:29:18 2019 From: john.r.rose at oracle.com (John Rose) Date: Tue, 13 Aug 2019 10:29:18 -0700 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> Message-ID: <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> +100 One nit: <\ u 0 0 2 0> is not an escape sequence and so should not be displayed next to <\ 0 4 0>. The former is processed before tokenization globally while the latter is processed inside strings only. This confusing fact is an additional motivation for <\ s> since most people don?t know what <\ u 0 0 2 0> actually does and some would attempt to use it if not presented with a clean and clear alternative like <\ s>. Another nit: <\ LT> (LT = LIneTerminator) is slightly more correct than three rules showing precursors of LT because those precursors have been replaced by LT before escape sequences are processed in string literals. The corresponding library method should say something like what you wrote. Substantive suggestion: I think <\ LT> could usefully gobble any unescaped horizontal space that immediately follows the LT. Also unescaped spaces before the <\ LT> could be gobbled. This would allow users freedom in arranging the line continuation escapes in a clear fashion (perhaps aligned visually) and still have complete control over content. The downside of this suggestion is that <\ s> would be required to mark spaces at line continuation edges which are desired as content and not just layout. I am thinking of the way multi line C preprocessor definitions are frequently formatted for clarity. The line continuations are visually bracketed away from the content, of the space around the continuations can be classified as non-content. This would be pleasant to offer as an option to text block authors. OTOH gobbling spaces around <\ LT> could be felt as a surprise. OTOOH trailing spaces are illegal in a text block currently, so allowing them as stripped layout control before <\ LT> is arguably intuitive?why else could it mean? So I suggest deleting all of where LWS is either space or tab, unescaped of course. > On Aug 13, 2019, at 5:46 AM, Jim Laskey wrote: > > https://bugs.openjdk.java.net/browse/JDK-8227870 > > Comment back to this list, thank you. > > Cheers, > > -- Jim > > > From james.laskey at oracle.com Tue Aug 13 17:53:27 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Tue, 13 Aug 2019 14:53:27 -0300 Subject: Comment received on amber-spec-comments In-Reply-To: References: Message-ID: <593CC8E5-8E51-4115-948A-583C947FA9AF@oracle.com> I should mention up front that text blocks are a preview feature and this open discussion is important before we lock the feature down. Unlike the multi-faceted raw string literals, text blocks attempt to do one thing well; provide a literal that makes it easy to express multiple lines of text. Hence, developers should really be thinking of text blocks as being more like the content of a text file and less like a string literal. That is, ideal for streaming and maybe less so for string manipulation. To counter the suggested examples, here is a reasonable example where that final line terminator works well; String part1 = """ Mary had a little lamb Whose fleece was white as snow. """; String part2 = """ And, everywhere that Mary went The lamb was sure to go. """; String verse = part1 + part2; The result is exactly what one would intuitively expect; Mary had a little lamb? Whose fleece was white as snow.? And, everywhere that Mary went? The lamb was sure to go.? Let's look at the origin of the final line terminator in detail. Take the following example, noting the location of the line terminators (?); String x = """ Mary had a little lamb? Whose fleece was white as snow.? """; The closing delimiter is preceded by the line terminator on the prior line. Since the line terminator is not part of closing delimiter, this final line terminator is actually part of the string content. The result here is "Mary had a little lamb?Whose fleece was white as snow.?". The lines all end with line terminators. Let's try a new example where we move the closing delimiter up to the prior line; String y = """ Mary had a little lamb? Whose fleece was white as snow."""; In this case, the closing delimiter is preceded by the ".". Thus, the result in this case is "Mary had a little lamb?Whose fleece was white as snow.". The lines are all joined with line terminators. Once you take this on mentally, it's easy to apply. Further, if the compiler were to strip away that final line terminator in the first case, there would be no way to distinguish the origins of x and y. This minimizing the loss of information is also an important part of extending text block design. This all said, text blocks were carefully designed to be the foundation for further string literal evolution. In a upcoming proposal (https://bugs.openjdk.java.net/browse/JDK-8227870 ) we would like to introduce a new escape sequence that can also be used to govern the presence of the final line terminator. The escape sequence \ indicates that the line terminator is to be ignored/removed. String z = """ Mary had a little lamb Whose fleece was white as snow.\ """; z, in this case, will have the same value as y above. That is. "Mary had a little lamb?Whose fleece was white as snow.". Comments on specific points inline below. Cheers, -- Jim > Hello, > > > > This ist the first time I'm writing to the OpenJDK devs and I'm not sure if > this is the correct place to do so, I apologise if it isn't. > > I recently saw the > (http://cr.openjdk.java.net/~jlaskey/Strings/TextBlocksGuide_v8.html ). > > First I'm really glad that you moved away from the String Literal proposal > and especially that you're not planning to to use the backtick as marker > anymore. > > The backtick is a dead-key on many keyboards and would be inconvenient to > type. > > > > However I was quite bothered that newlines are added at the end of > textblocks when the end-marker (""") is on a new line. > > > > Is there a good reason for this? > > > > I think developers would have some benefits if it wouldn't do that. In > consequence the end-marker would need to on its own line, the same way as > the start-marker > > needs to be followed by a line-terminator. > > > > - An automatic final newline is often not desirable. E.g. HTML or SQL or > prose that we wish to include as a textblock. Some examples in the > seem to be unnaturally constructed to > fit the resulting textblocks. Some of the style guide examples are contrived to make a point, but I believe "unnatural" is countered by my commentary above. It could be that Python leads you to work one way and that Java does it an alternative way. Programming languages are like that. That's why we have so many. > > - The final newline it is even in the way. E.g. Valid Python code or when > concatenating two textblocks. Without newlines at the end, concatenation > would feel similar as we are used to it now. > > - It's harder and inconvenient to remove a newline after creating the > textblock. The textblock will often be used as a static final constant > field. There's is currently no easy way to do this with the JDK alone. It > would require import to org.apache.commons.lang3.StringUtils::chomp. In > contrast it would be easy to add a final newline if it was really required. > Simply adding an empty line before the end-marker. String x = """ Mary had a little lamb? Whose fleece was white as snow.? """.stripTrailing(); > > - . The resulting String would always entail > the textblock between the markers. If there's a final newline, it would be > clearly visible as a newline in the block. Valid point, but design is a choice. There can be more than one correct answer of equal value. Others might see the extra line as causing the closing delimiter to be disjoint. > > - Singleline textblocks would be visually appealing. It would be and look > consistent with multiline textblocks. You could express indents in > singleline textblocks without having to use String::indent after creation. > > Stripping of leading/trailing blank lines was considered, but blank lines are often desired. We chose be as loss-less as possible. > > For examples: > > private static final String PARAGRAPH = """ > > >

"This is a paragraph!"

> > """; > > > > Would result in: > > |

"This is a paragraph!"

> > > > Kind regards, > > > > Simon > > >> On Aug 12, 2019, at 10:26 AM, Brian Goetz wrote: >> >> The following comment was received on amber-spec-comments: >> >> https://mail.openjdk.java.net/pipermail/amber-spec-comments/2019-August/000028.html >> >> > From alex.buckley at oracle.com Tue Aug 13 20:51:57 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 13 Aug 2019 13:51:57 -0700 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> Message-ID: - Title: "Escape Sequences for Line Continuation and White Space (Preview)" (the narrative term is "white space" per the JLS and JEP 355; the only time the ` ` character after "white" is missing is in the name of the grammar production WhiteSpace) - Goal: "Improve the the observability of the space (U+0020) character in string literals." -- not sure that's ever been a huge problem, and it distracts from the real deal which is retaining white space in text blocks. Recommend deletion. - \040 is introduced as the "space escape sequence". Please don't confuse people by making them look in JLS 3.10.6 for a non-existent sequence; please reuse how JEP 355 introduced \040. - In the retaining white space section, the argument is slightly mis-ordered. You show the \040\040\040\040 example, then say it's arcane (yes) and the \040 escape is perplexing (yes) and that readability could be enhanced (yes) but then you double down on \040 by showing it as a fence in the `red \040` example. Better to show the \040\040\040\040 example, then say "don't worry, you don't need that whole ugly sequence, you only need one \040, it's called a fence, look:" then show the `red \040` example, THEN say that \040 is arcane and a better escape is needed. - "Strings that require using backslash as a character can use the \\ escape sequence. This is also true at the end of line." -- please say that \\ works because Java does not do recursive processing of escape sequences -- once \\ has been processed to \, the \ and the following NL are NOT further processed to a line terminator. Being explicit about how escape processing works will keep us sane as we grow the "escape language" whose processing is split across JLS and API. - General: example code shown in the Motivation should be reused in the Description but with the new escape sequences. You use lorem ipsum for a concatenated string literal in Motivation, use it again in Description! Same for the red green blue example, which is much better than x yy zzz. - The Alternatives for Line Continuation talk about long string literals, then show text block examples. Since \ works in a string literal, I was expecting a story which ignores text blocks and talks only of improved string literals. Too many things varying at once. - Reading "Replacing marker sequence (plus newline) with empty string", I realized the `...` is another kind of fence -- rather than preventing trailing white space for going beyond itself (the definition of a fence), it prevents the entire line going beyond itself. Consider saying "In a text block, the newline is an implicit fence; a more explicit fence can be made not just with \ but with any character sequence, e.g. `...` or `$`, which is then replaced along with the immediately-following newline." Alex On 8/13/2019 5:46 AM, Jim Laskey wrote: > https://bugs.openjdk.java.net/browse/JDK-8227870 > > Comment back to this list, thank you. > > Cheers, > > -- Jim > > > From james.laskey at oracle.com Wed Aug 14 13:21:20 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Wed, 14 Aug 2019 10:21:20 -0300 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> Message-ID: Thank you Alex. > On Aug 13, 2019, at 5:51 PM, Alex Buckley wrote: > > - Title: "Escape Sequences for Line Continuation and White Space (Preview)" (the narrative term is "white space" per the JLS and JEP 355; the only time the ` ` character after "white" is missing is in the name of the grammar production WhiteSpace) Done. > > - Goal: "Improve the the observability of the space (U+0020) character in string literals." -- not sure that's ever been a huge problem, and it distracts from the real deal which is retaining white space in text blocks. Recommend deletion. > Done. > - \040 is introduced as the "space escape sequence". Please don't confuse people by making them look in JLS 3.10.6 for a non-existent sequence; please reuse how JEP 355 introduced \040. > Done. > - In the retaining white space section, the argument is slightly mis-ordered. You show the \040\040\040\040 example, then say it's arcane (yes) and the \040 escape is perplexing (yes) and that readability could be enhanced (yes) but then you double down on \040 by showing it as a fence in the `red \040` example. Better to show the \040\040\040\040 example, then say "don't worry, you don't need that whole ugly sequence, you only need one \040, it's called a fence, look:" then show the `red \040` example, THEN say that \040 is arcane and a better escape is needed. > Done. > - "Strings that require using backslash as a character can use the \\ escape sequence. This is also true at the end of line." -- please say that \\ works because Java does not do recursive processing of escape sequences -- once \\ has been processed to \, the \ and the following NL are NOT further processed to a line terminator. Being explicit about how escape processing works will keep us sane as we grow the "escape language" whose processing is split across JLS and API. > Done. > - General: example code shown in the Motivation should be reused in the Description but with the new escape sequences. You use lorem ipsum for a concatenated string literal in Motivation, use it again in Description! Same for the red green blue example, which is much better than x yy zzz. > Done. > - The Alternatives for Line Continuation talk about long string literals, then show text block examples. Since \ works in a string literal, I was expecting a story which ignores text blocks and talks only of improved string literals. Too many things varying at once. > This one is still ponies and horses to me. Not sure how to approach. > - Reading "Replacing marker sequence (plus newline) with empty string", I realized the `...` is another kind of fence -- rather than preventing trailing white space for going beyond itself (the definition of a fence), it prevents the entire line going beyond itself. Consider saying "In a text block, the newline is an implicit fence; a more explicit fence can be made not just with \ but with any character sequence, e.g. `...` or `$`, which is then replaced along with the immediately-following newline." > Reorganized section to be more to the point. > Alex > > On 8/13/2019 5:46 AM, Jim Laskey wrote: >> https://bugs.openjdk.java.net/browse/JDK-8227870 >> Comment back to this list, thank you. >> Cheers, >> -- Jim From john.r.rose at oracle.com Wed Aug 14 20:55:38 2019 From: john.r.rose at oracle.com (John Rose) Date: Wed, 14 Aug 2019 13:55:38 -0700 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> Message-ID: <7E891CD8-BBD8-49FB-83DA-3CB7653EF08B@oracle.com> On Aug 13, 2019, at 10:29 AM, John Rose wrote: > > So I suggest deleting all of where LWS is either space or tab, unescaped of course. > Just to be clear about my suggestion for stripping additional incidental whitespace before and/or after <\ LT>, here?s an example for using a rule that expand stripping: String story = """ "When I use a word," Humpty Dumpty said, \ in rather a scornful tone, "it means just what I \ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you \ can make words mean so many different things." "The question is," said Humpty Dumpty, \ "which is to be master - that's all." """; Here, all the spaces *before* the backslashes are treated as incidental. They are beneficial in allowing the programmer to lay them out separately from the content. The dots show which spaces are incidental (in both existing rules and under my suggestion)l: String story = """ ...."When I use a word," Humpty Dumpty said,.........\ .... in rather a scornful tone, "it means just what I.\ .... choose it to mean - neither more nor less.? ...."The question is," said Alice, "whether you......\ .... can make words mean so many different things." ...."The question is," said Humpty Dumpty,...........\ .... "which is to be master - that's all." ...."""; Here is the same content but without the extra incidental spaces, which conforms to the current proposal by Jim: String story = """ "When I use a word," Humpty Dumpty said, \ in rather a scornful tone, "it means just what I \ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you \ can make words mean so many different things." "The question is," said Humpty Dumpty, \ "which is to be master - that's all." """; Equivalently, and a bit more legibly, and also within the bounds of Jim's proposal: String story = """ "When I use a word," Humpty Dumpty said,\ in rather a scornful tone, "it means just what I\ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you\ can make words mean so many different things." "The question is," said Humpty Dumpty,\ "which is to be master - that's all." """; The intended content is, of course: String story = """ "When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean - neither more nor less.? "The question is," said Alice, "whether you can make words mean so many different things." "The question is," said Humpty Dumpty, "which is to be master - that's all." """; Incidental space could also be stripped after <\ LT> as well as before. I think that the value of aligning the backslashes is the main benefit here, so stripping *before* <\ LT> is more important than after. But stripping incidentals after would allow the programmer to lay out continuation lines at a different left margin, which can also be a help to readability, although the <\ s> sequences undo the benefit: String story = """ "When I use a word," Humpty Dumpty said, \ \sin rather a scornful tone, "it means just what I \ \schoose it to mean - neither more nor less.? "The question is," said Alice, "whether you \ \scan make words mean so many different things." "The question is," said Humpty Dumpty, \ \s"which is to be master - that's all." """; So I?ll amend my suggestion to something a little trickier: is stripped as incidental whitespace, but in the case of , the final character of linear whitespace (aka horizontal whitespace) is significant content, not incidental. This allows the obscuring <\ s> to be removed in the example: String story = """ "When I use a word," Humpty Dumpty said, \ in rather a scornful tone, "it means just what I \ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you \ can make words mean so many different things." "The question is," said Humpty Dumpty, \ "which is to be master - that's all." """; Here is the extra incidental space marked as dots: String story = """ ...."When I use a word," Humpty Dumpty said,.........\ ........ in rather a scornful tone, "it means just what I.\ ........ choose it to mean - neither more nor less.? ...."The question is," said Alice, "whether you......\ ........ can make words mean so many different things." ...."The question is," said Humpty Dumpty,...........\ ........ "which is to be master - that's all." ...."""; The suggested rule allows the author to adjust the broken line endings for maximum readability, and the elision of the incidental space flows with them. Meanwhile, if the content has no spaces (perhaps it?s some kind fo binary resource) the user cannot indent the continuation lines, but can still space out the backslash into a proudly separate position from the content: String hexData = """ 000102030405060708090a0b0c0d0e0f101112131415 \ 161718191a1b1c1d1e1f202122232425262728292a2b2c \ 2d2e2f303132333435363738393a3b3c3d3e3f40 \ 4142434445464748494a4b4c4d4e4f505152535455 \ """; assert !hexData.contains(" "); The basic motivation should be clear: Give users tools to lay out delimiters clearly separate from content. Whether it?s worth adding the extra twist to the rule about <\ LT> I will leave for others to decide. HTH. ? John From james.laskey at oracle.com Thu Aug 15 15:19:12 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 15 Aug 2019 12:19:12 -0300 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <7E891CD8-BBD8-49FB-83DA-3CB7653EF08B@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> <7E891CD8-BBD8-49FB-83DA-3CB7653EF08B@oracle.com> Message-ID: <49C15995-1FA8-4DBD-B0E9-3D1B5B29D51D@oracle.com> Assuming the proposal is (keep last white space), I see the light but I see the dark here as well (and maybe not enough here to switch horses.) String hexData = """ 000102030405060708090a0b0c0d0e0f101112131415 \ 161718191a1b1c1d1e1f202122232425262728292a2b2c \ 2d2e2f303132333435363738393a3b3c3d3e3f40 \ 4142434445464748494a4b4c4d4e4f505152535455 \ """; This sort of pattern is high maintenance; aligning backslashes. I think a style guide would likely discourage this as a general pattern (even though I like the aesthetics.) I tend to favour the Jim patterns you mentioned, mostly because they are precise. There is no question here about what is kept and what is discarded. String story = """ "When I use a word," Humpty Dumpty said, \ in rather a scornful tone, "it means just what I \ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you \ can make words mean so many different things." "The question is," said Humpty Dumpty, \ "which is to be master - that's all." """; String story = """ "When I use a word," Humpty Dumpty said,\ in rather a scornful tone, "it means just what I\ choose it to mean - neither more nor less.? "The question is," said Alice, "whether you\ can make words mean so many different things." "The question is," said Humpty Dumpty,\ "which is to be master - that's all." """; There is a another case which you didn't mention; String story = """ "When I use a word," Humpty Dumpty said,\ in rather a scornful tone, "it means just what I\ choose it to mean - neither more nor less.?\ "The question is," said Alice, "whether you\ can make words mean so many different things."\ "The question is," said Humpty Dumpty,\ "which is to be master - that's all." """; What does do you do here? Do you stop at the second line terminator and, if so, what happens to the white space on the next line. This is where precision would make it cleaner/clearer. THE one case where your proposal wins for me is when used with the string literals. As proposed in the JEP, we have to continue string literals immediately at the left margin. String str = "A line that may need to \ continue on the next line."; Using your proposal we could do the following; String str = "A line that may need to \ continue on the next line."; Cheers, -- Jim > On Aug 14, 2019, at 5:55 PM, John Rose wrote: > > On Aug 13, 2019, at 10:29 AM, John Rose > wrote: >> >> So I suggest deleting all of where LWS is either space or tab, unescaped of course. >> > > Just to be clear about my suggestion for stripping additional incidental whitespace before > and/or after <\ LT>, here?s an example for using a rule that expand stripping: > > String story = """ > "When I use a word," Humpty Dumpty said, \ > in rather a scornful tone, "it means just what I \ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you \ > can make words mean so many different things." > "The question is," said Humpty Dumpty, \ > "which is to be master - that's all." > """; > > Here, all the spaces *before* the backslashes are treated as incidental. They are beneficial > in allowing the programmer to lay them out separately from the content. The dots show > which spaces are incidental (in both existing rules and under my suggestion)l: > > String story = """ > ...."When I use a word," Humpty Dumpty said,.........\ > .... in rather a scornful tone, "it means just what I.\ > .... choose it to mean - neither more nor less.? > ...."The question is," said Alice, "whether you......\ > .... can make words mean so many different things." > ...."The question is," said Humpty Dumpty,...........\ > .... "which is to be master - that's all." > ...."""; > > Here is the same content but without the extra incidental spaces, which conforms to the current > proposal by Jim: > > > String story = """ > "When I use a word," Humpty Dumpty said, \ > in rather a scornful tone, "it means just what I \ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you \ > can make words mean so many different things." > "The question is," said Humpty Dumpty, \ > "which is to be master - that's all." > """; > > Equivalently, and a bit more legibly, and also within the bounds of Jim's proposal: > > String story = """ > "When I use a word," Humpty Dumpty said,\ > in rather a scornful tone, "it means just what I\ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you\ > can make words mean so many different things." > "The question is," said Humpty Dumpty,\ > "which is to be master - that's all." > """; > > The intended content is, of course: > String story = """ > "When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you can make words mean so many different things." > "The question is," said Humpty Dumpty, "which is to be master - that's all." > """; > > Incidental space could also be stripped after <\ LT> as well as before. I think that the value of aligning > the backslashes is the main benefit here, so stripping *before* <\ LT> is more important than after. > But stripping incidentals after would allow the programmer to lay out continuation lines at a different > left margin, which can also be a help to readability, although the <\ s> sequences undo the benefit: > > > String story = """ > "When I use a word," Humpty Dumpty said, \ > \sin rather a scornful tone, "it means just what I \ > \schoose it to mean - neither more nor less.? > "The question is," said Alice, "whether you \ > \scan make words mean so many different things." > "The question is," said Humpty Dumpty, \ > \s"which is to be master - that's all." > """; > > So I?ll amend my suggestion to something a little trickier: is stripped > as incidental whitespace, but in the case of , the final character > of linear whitespace (aka horizontal whitespace) is significant content, not incidental. > This allows the obscuring <\ s> to be removed in the example: > > > String story = """ > "When I use a word," Humpty Dumpty said, \ > in rather a scornful tone, "it means just what I \ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you \ > can make words mean so many different things." > "The question is," said Humpty Dumpty, \ > "which is to be master - that's all." > """; > > Here is the extra incidental space marked as dots: > > String story = """ > ...."When I use a word," Humpty Dumpty said,.........\ > ........ in rather a scornful tone, "it means just what I.\ > ........ choose it to mean - neither more nor less.? > ...."The question is," said Alice, "whether you......\ > ........ can make words mean so many different things." > ...."The question is," said Humpty Dumpty,...........\ > ........ "which is to be master - that's all." > ...."""; > > The suggested rule allows the author to adjust the broken line endings for maximum readability, > and the elision of the incidental space flows with them. > > Meanwhile, if the content has no spaces (perhaps it?s some kind fo binary resource) the user > cannot indent the continuation lines, but can still space out the backslash into a proudly separate > position from the content: > > String hexData = """ > 000102030405060708090a0b0c0d0e0f101112131415 \ > 161718191a1b1c1d1e1f202122232425262728292a2b2c \ > 2d2e2f303132333435363738393a3b3c3d3e3f40 \ > 4142434445464748494a4b4c4d4e4f505152535455 \ > """; > assert !hexData.contains(" "); > > The basic motivation should be clear: Give users tools to lay out delimiters clearly separate > from content. Whether it?s worth adding the extra twist to the rule about <\ LT> I will leave for > others to decide. > > HTH. > > ? John From james.laskey at oracle.com Thu Aug 15 15:32:39 2019 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 15 Aug 2019 12:32:39 -0300 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <49C15995-1FA8-4DBD-B0E9-3D1B5B29D51D@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> <7E891CD8-BBD8-49FB-83DA-3CB7653EF08B@oracle.com> <49C15995-1FA8-4DBD-B0E9-3D1B5B29D51D@oracle.com> Message-ID: s/What does do you do here?/What so you do here/ > On Aug 15, 2019, at 12:19 PM, Jim Laskey wrote: > > Assuming the proposal is (keep last white space), I see the light but I see the dark here as well (and maybe not enough here to switch horses.) > > String hexData = """ > 000102030405060708090a0b0c0d0e0f101112131415 \ > 161718191a1b1c1d1e1f202122232425262728292a2b2c \ > 2d2e2f303132333435363738393a3b3c3d3e3f40 \ > 4142434445464748494a4b4c4d4e4f505152535455 \ > """; > > This sort of pattern is high maintenance; aligning backslashes. I think a style guide would likely discourage this as a general pattern (even though I like the aesthetics.) I tend to favour the Jim patterns you mentioned, mostly because they are precise. There is no question here about what is kept and what is discarded. > > String story = """ > "When I use a word," Humpty Dumpty said, \ > in rather a scornful tone, "it means just what I \ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you \ > can make words mean so many different things." > "The question is," said Humpty Dumpty, \ > "which is to be master - that's all." > """; > String story = """ > "When I use a word," Humpty Dumpty said,\ > in rather a scornful tone, "it means just what I\ > choose it to mean - neither more nor less.? > "The question is," said Alice, "whether you\ > can make words mean so many different things." > "The question is," said Humpty Dumpty,\ > "which is to be master - that's all." > """; > > There is a another case which you didn't mention; > > String story = """ > "When I use a word," Humpty Dumpty said,\ > in rather a scornful tone, "it means just what I\ > choose it to mean - neither more nor less.?\ > > "The question is," said Alice, "whether you\ > can make words mean so many different things."\ > > "The question is," said Humpty Dumpty,\ > "which is to be master - that's all." > """; > What does do you do here? Do you stop at the second line terminator and, if so, what happens to the white space on the next line. This is where precision would make it cleaner/clearer. > > THE one case where your proposal wins for me is when used with the string literals. As proposed in the JEP, we have to continue string literals immediately at the left margin. > > String str = "A line that may need to \ > continue on the next line."; > > Using your proposal we could do the following; > > String str = "A line that may need to \ > continue on the next line."; > > Cheers, > > -- Jim > > > >> On Aug 14, 2019, at 5:55 PM, John Rose > wrote: >> >> On Aug 13, 2019, at 10:29 AM, John Rose > wrote: >>> >>> So I suggest deleting all of where LWS is either space or tab, unescaped of course. >>> >> >> Just to be clear about my suggestion for stripping additional incidental whitespace before >> and/or after <\ LT>, here?s an example for using a rule that expand stripping: >> >> String story = """ >> "When I use a word," Humpty Dumpty said, \ >> in rather a scornful tone, "it means just what I \ >> choose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you \ >> can make words mean so many different things." >> "The question is," said Humpty Dumpty, \ >> "which is to be master - that's all." >> """; >> >> Here, all the spaces *before* the backslashes are treated as incidental. They are beneficial >> in allowing the programmer to lay them out separately from the content. The dots show >> which spaces are incidental (in both existing rules and under my suggestion)l: >> >> String story = """ >> ...."When I use a word," Humpty Dumpty said,.........\ >> .... in rather a scornful tone, "it means just what I.\ >> .... choose it to mean - neither more nor less.? >> ...."The question is," said Alice, "whether you......\ >> .... can make words mean so many different things." >> ...."The question is," said Humpty Dumpty,...........\ >> .... "which is to be master - that's all." >> ...."""; >> >> Here is the same content but without the extra incidental spaces, which conforms to the current >> proposal by Jim: >> >> >> String story = """ >> "When I use a word," Humpty Dumpty said, \ >> in rather a scornful tone, "it means just what I \ >> choose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you \ >> can make words mean so many different things." >> "The question is," said Humpty Dumpty, \ >> "which is to be master - that's all." >> """; >> >> Equivalently, and a bit more legibly, and also within the bounds of Jim's proposal: >> >> String story = """ >> "When I use a word," Humpty Dumpty said,\ >> in rather a scornful tone, "it means just what I\ >> choose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you\ >> can make words mean so many different things." >> "The question is," said Humpty Dumpty,\ >> "which is to be master - that's all." >> """; >> >> The intended content is, of course: >> String story = """ >> "When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you can make words mean so many different things." >> "The question is," said Humpty Dumpty, "which is to be master - that's all." >> """; >> >> Incidental space could also be stripped after <\ LT> as well as before. I think that the value of aligning >> the backslashes is the main benefit here, so stripping *before* <\ LT> is more important than after. >> But stripping incidentals after would allow the programmer to lay out continuation lines at a different >> left margin, which can also be a help to readability, although the <\ s> sequences undo the benefit: >> >> >> String story = """ >> "When I use a word," Humpty Dumpty said, \ >> \sin rather a scornful tone, "it means just what I \ >> \schoose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you \ >> \scan make words mean so many different things." >> "The question is," said Humpty Dumpty, \ >> \s"which is to be master - that's all." >> """; >> >> So I?ll amend my suggestion to something a little trickier: is stripped >> as incidental whitespace, but in the case of , the final character >> of linear whitespace (aka horizontal whitespace) is significant content, not incidental. >> This allows the obscuring <\ s> to be removed in the example: >> >> >> String story = """ >> "When I use a word," Humpty Dumpty said, \ >> in rather a scornful tone, "it means just what I \ >> choose it to mean - neither more nor less.? >> "The question is," said Alice, "whether you \ >> can make words mean so many different things." >> "The question is," said Humpty Dumpty, \ >> "which is to be master - that's all." >> """; >> >> Here is the extra incidental space marked as dots: >> >> String story = """ >> ...."When I use a word," Humpty Dumpty said,.........\ >> ........ in rather a scornful tone, "it means just what I.\ >> ........ choose it to mean - neither more nor less.? >> ...."The question is," said Alice, "whether you......\ >> ........ can make words mean so many different things." >> ...."The question is," said Humpty Dumpty,...........\ >> ........ "which is to be master - that's all." >> ...."""; >> >> The suggested rule allows the author to adjust the broken line endings for maximum readability, >> and the elision of the incidental space flows with them. >> >> Meanwhile, if the content has no spaces (perhaps it?s some kind fo binary resource) the user >> cannot indent the continuation lines, but can still space out the backslash into a proudly separate >> position from the content: >> >> String hexData = """ >> 000102030405060708090a0b0c0d0e0f101112131415 \ >> 161718191a1b1c1d1e1f202122232425262728292a2b2c \ >> 2d2e2f303132333435363738393a3b3c3d3e3f40 \ >> 4142434445464748494a4b4c4d4e4f505152535455 \ >> """; >> assert !hexData.contains(" "); >> >> The basic motivation should be clear: Give users tools to lay out delimiters clearly separate >> from content. Whether it?s worth adding the extra twist to the rule about <\ LT> I will leave for >> others to decide. >> >> HTH. >> >> ? John > From brian.goetz at oracle.com Thu Aug 15 17:06:02 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 13:06:02 -0400 Subject: Draft specification for java.lang.Record Message-ID: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Draft spec, please comment. /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang; /** * This is the common base class of all Java language record classes. * *

More information about records, including descriptions of the * implicitly declared methods synthesized by the compiler, can be * found in section 8.10 of * The Java™Language Specification. * *

A record classis a shallowly immutable, transparent carrier for * a fixed set of values, called the record components. The Java™* language provides concise syntax for declaring record classes, whereby the * record components are declared in the record header. The list of record * components declared in the record header form the record descriptor. * *

A record class has the following mandated members: a public canonical * constructor, whose descriptor is the same as the record descriptor; * a private static field corresponding to each component, whose name and * type are the same as that of the component; a public accessor method * corresponding to each component, whose name and return type are the same as * that of the component. If not explicitly declared in the body of the record, * implicit implementations for these members are provided. * *

The implicit declaration of the canonical constructor initializes the * component fields from the corresponding constructor arguments. The implicit * declaration of the accessor methods returns the value of the corresponding * component field. The implicit declaration of the {@link Object#equals(Object)}, * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived * from all of the component fields. * *

The primary reasons to provide an explicit declaration for the * canonical constructor or accessor methods are to validate constructor * arguments, perform defensive copies on mutable components, or normalize groups * of components (such as reducing a rational number to lowest terms.) If any * of these are provided explicitly. * *

For all record classes, the following invariant must hold: if a record R's * components are {@code c1, c2, ... cn}, then if a record instance is copied * as follows: *

* R copy = new R(r.c1(), r.c2(), ..., 
r.cn()); * 
* then it must be the case that {@code r.equals(copy)}. * * @jls 8.10 * @since 14 */ public abstract class Record { /** * Indicates whether some other object is "equal to" this one. In addition * to the general contract of {@link Object#equals(Object)}, * record classes must further participate in the invariant that when * a record instance is "copied" by passing the result of the record component * accessor methods to the canonical constructor, the resulting copy is * equal to the original instance. * * @implNote * The implicitly provided implementation returns {@code true} if and * only if the argument is an instance of the same record type as this object, * and each component of this record is equal to the corresponding component * of the argument, according to {@link Object#equals(Object)} for components * whose types are reference types, and {@code ==} for components whose * types are primitive types. * * @see Object#equals(Object) * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. */ @Override public abstract boolean equals(Object obj); /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a hash code value derived * by combining the hash code value for all the components, according to * {@link Object#hashCode()} for components whose types are reference types, * or the primitive wrapper hash code for components whose types are primitive * types. * * @see Object#hashCode() * * @return a hash code value for this object. */ @Override public abstract int hashCode(); /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a string that is derived * from the name of the record class and the names and string representations * of all the components, according to {@link Object#toString()} for components * whose types are reference types, and the primitive wrapper {@code toString} * method for components whose types are primitive types. * * @see Object#toString() () * * @return a string representation of the object. */ @Override public abstract String toString(); } From amaembo at gmail.com Thu Aug 15 17:15:40 2019 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 16 Aug 2019 00:15:40 +0700 Subject: Draft specification for java.lang.Record In-Reply-To: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: Hello! >a private static field corresponding to each component Final instead of static? With best regards, Tagir Valeev ??, 16 ???. 2019 ?., 0:06 Brian Goetz : > Draft spec, please comment. > > > /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package java.lang; > /** * This is the common base class of all Java language record classes. * *

More information about records, including descriptions of the * implicitly declared methods synthesized by the compiler, can be * found in section 8.10 of * The Java™ Language Specification. * *

A record class is a shallowly immutable, transparent carrier for * a fixed set of values, called the record components. The Java™ * language provides concise syntax for declaring record classes, whereby the * record components are declared in the record header. The list of record * components declared in the record header form the record descriptor. * *

A record class has the following mandated members: a public canonical * constructor, whose descriptor is the same as the record descriptor; * a private static field corresponding to each component, whose name and * type are the same as that of the component; a public accessor method * corresponding to each component, whose name and return type are the same as * that of the component. If not explicitly declared in the body of the record, * implicit implementations for these members are provided. * *

The implicit declaration of the canonical constructor initializes the * component fields from the corresponding constructor arguments. The implicit * declaration of the accessor methods returns the value of the corresponding * component field. The implicit declaration of the {@link Object#equals(Object)}, * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived * from all of the component fields. * *

The primary reasons to provide an explicit declaration for the * canonical constructor or accessor methods are to validate constructor * arguments, perform defensive copies on mutable components, or normalize groups * of components (such as reducing a rational number to lowest terms.) If any * of these are provided explicitly. * *

For all record classes, the following invariant must hold: if a record R's * components are {@code c1, c2, ... cn}, then if a record instance is copied * as follows: *

 *     R copy = new R(r.c1(), r.c2(), ..., r.cn()); * 
* then it must be the case that {@code r.equals(copy)}. * * @jls 8.10 * @since 14 */public abstract class Record { > /** * Indicates whether some other object is "equal to" this one. In addition * to the general contract of {@link Object#equals(Object)}, * record classes must further participate in the invariant that when * a record instance is "copied" by passing the result of the record component * accessor methods to the canonical constructor, the resulting copy is * equal to the original instance. * * @implNote * The implicitly provided implementation returns {@code true} if and * only if the argument is an instance of the same record type as this object, * and each component of this record is equal to the corresponding component * of the argument, according to {@link Object#equals(Object)} for components * whose types are reference types, and {@code ==} for components whose * types are primitive types. * * @see Object#equals(Object) * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. */ @Override public abstract boolean equals(Object obj); > > /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a hash code value derived * by combining the hash code value for all the components, according to * {@link Object#hashCode()} for components whose types are reference types, * or the primitive wrapper hash code for components whose types are primitive * types. * * @see Object#hashCode() * * @return a hash code value for this object. */ @Override public abstract int hashCode(); > > /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a string that is derived * from the name of the record class and the names and string representations * of all the components, according to {@link Object#toString()} for components * whose types are reference types, and the primitive wrapper {@code toString} * method for components whose types are primitive types. * * @see Object#toString() () * * @return a string representation of the object. */ @Override public abstract String toString(); > } > > From amaembo at gmail.com Thu Aug 15 17:23:27 2019 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 16 Aug 2019 00:23:27 +0700 Subject: Draft specification for java.lang.Record In-Reply-To: References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: Hello! equals() spec says: The implicitly provided implementation returns {@code true} if and * only if the argument is an instance of the same record type as this object, * and each component of this record is equal to the corresponding component * of the argument, according to {@link Object#equals(Object)} for components * whose types are reference types, and {@code ==} for components whose * types are primitive types. * This contradicts the class spec for float/double fields (in particular, NaN values): *

For all record classes, the following invariant must hold: if a record R's * components are {@code c1, c2, ... cn}, then if a record instance is copied * as follows: *

 *     R copy = new R(r.c1(),
r.c2(), ..., r.cn()); * 
* then it must be the case that {@code r.equals(copy)}. E.g. record R(double d) {} R r1 = new R(Double.NaN); R r2 = new R(r1.d()); assert r1.equals(r2); // fails as R.d is a primitive field and r1.d != r2.d With best regards, Tagir Valeev. On Fri, Aug 16, 2019 at 12:15 AM Tagir Valeev wrote: > Hello! > > >a private static field corresponding to each component > > Final instead of static? > > With best regards, > Tagir Valeev > > ??, 16 ???. 2019 ?., 0:06 Brian Goetz : > >> Draft spec, please comment. >> >> >> /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package java.lang; >> /** * This is the common base class of all Java language record classes. * *

More information about records, including descriptions of the * implicitly declared methods synthesized by the compiler, can be * found in section 8.10 of * The Java™ Language Specification. * *

A record class is a shallowly immutable, transparent carrier for * a fixed set of values, called the record components. The Java™ * language provides concise syntax for declaring record classes, whereby the * record components are declared in the record header. The list of record * components declared in the record header form the record descriptor. * *

A record class has the following mandated members: a public canonical * constructor, whose descriptor is the same as the record descriptor; * a private static field corresponding to each component, whose name and * type are the same as that of the component; a public accessor method * corresponding to each component, whose name and return type are the same as * that of the component. If not explicitly declared in the body of the record, * implicit implementations for these members are provided. * *

The implicit declaration of the canonical constructor initializes the * component fields from the corresponding constructor arguments. The implicit * declaration of the accessor methods returns the value of the corresponding * component field. The implicit declaration of the {@link Object#equals(Object)}, * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived * from all of the component fields. * *

The primary reasons to provide an explicit declaration for the * canonical constructor or accessor methods are to validate constructor * arguments, perform defensive copies on mutable components, or normalize groups * of components (such as reducing a rational number to lowest terms.) If any * of these are provided explicitly. * *

For all record classes, the following invariant must hold: if a record R's * components are {@code c1, c2, ... cn}, then if a record instance is copied * as follows: *

 *     R copy = new R(r.c1(), r.c2(), ..., r.cn()); * 
* then it must be the case that {@code r.equals(copy)}. * * @jls 8.10 * @since 14 */public abstract class Record { >> /** * Indicates whether some other object is "equal to" this one. In addition * to the general contract of {@link Object#equals(Object)}, * record classes must further participate in the invariant that when * a record instance is "copied" by passing the result of the record component * accessor methods to the canonical constructor, the resulting copy is * equal to the original instance. * * @implNote * The implicitly provided implementation returns {@code true} if and * only if the argument is an instance of the same record type as this object, * and each component of this record is equal to the corresponding component * of the argument, according to {@link Object#equals(Object)} for components * whose types are reference types, and {@code ==} for components whose * types are primitive types. * * @see Object#equals(Object) * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. */ @Override public abstract boolean equals(Object obj); >> >> /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a hash code value derived * by combining the hash code value for all the components, according to * {@link Object#hashCode()} for components whose types are reference types, * or the primitive wrapper hash code for components whose types are primitive * types. * * @see Object#hashCode() * * @return a hash code value for this object. */ @Override public abstract int hashCode(); >> >> /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a string that is derived * from the name of the record class and the names and string representations * of all the components, according to {@link Object#toString()} for components * whose types are reference types, and the primitive wrapper {@code toString} * method for components whose types are primitive types. * * @see Object#toString() () * * @return a string representation of the object. */ @Override public abstract String toString(); >> } >> >> From brian.goetz at oracle.com Thu Aug 15 17:34:43 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 13:34:43 -0400 Subject: Draft specification for java.lang.Record In-Reply-To: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> Re-sending as plain text, since the formatting got mangled by mailers. /* ?* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ?* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ?* ?* This code is free software; you can redistribute it and/or modify it ?* under the terms of the GNU General Public License version 2 only, as ?* published by the Free Software Foundation.? Oracle designates this ?* particular file as subject to the "Classpath" exception as provided ?* by Oracle in the LICENSE file that accompanied this code. ?* ?* This code is distributed in the hope that it will be useful, but WITHOUT ?* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ?* FITNESS FOR A PARTICULAR PURPOSE.? See the GNU General Public License ?* version 2 for more details (a copy is included in the LICENSE file that ?* accompanied this code). ?* ?* You should have received a copy of the GNU General Public License version ?* 2 along with this work; if not, write to the Free Software Foundation, ?* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ?* ?* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ?* or visit www.oracle.com if you need additional information or have any ?* questions. ?*/ package java.lang; /** ?* This is the common base class of all Java language record classes. ?* ?*

More information about records, including descriptions of the ?* implicitly declared methods synthesized by the compiler, can be ?* found in section 8.10 of ?* The Java™ Language Specification. ?* ?*

A record class is a shallowly immutable, transparent carrier for ?* a fixed set of values, called the record components.? The Java™ ?* language provides concise syntax for declaring record classes, whereby the ?* record components are declared in the record header.? The list of record ?* components declared in the record header form the record descriptor. ?* ?*

A record class has the following mandated members: a public canonical ?* constructor, whose descriptor is the same as the record descriptor; ?* a private static field corresponding to each component, whose name and ?* type are the same as that of the component; a public accessor method ?* corresponding to each component, whose name and return type are the same as ?* that of the component.? If not explicitly declared in the body of the record, ?* implicit implementations for these members are provided. ?* ?*

The implicit declaration of the canonical constructor initializes the ?* component fields from the corresponding constructor arguments. The implicit ?* declaration of the accessor methods returns the value of the corresponding ?* component field.? The implicit declaration of the {@link Object#equals(Object)}, ?* {@link Object#hashCode()}, and {@link Object#toString()} methods are derived ?* from all of the component fields. ?* ?*

The primary reasons to provide an explicit declaration for the ?* canonical constructor or accessor methods are to validate constructor ?* arguments, perform defensive copies on mutable components, or normalize groups ?* of components (such as reducing a rational number to lowest terms.)? If any ?* of these are provided explicitly. ?* ?*

For all record classes, the following invariant must hold: if a record R's ?* components are {@code c1, c2, ... cn}, then if a record instance is copied ?* as follows: ?*

 ?*???? R copy = new R(r.c1(), r.c2(), ..., r.cn());
 ?* 
?* then it must be the case that {@code r.equals(copy)}. ?* ?* @jls 8.10 ?* @since 14 ?*/ public abstract class Record { ??? /** ???? * Indicates whether some other object is "equal to" this one.? In addition ???? * to the general contract of {@link Object#equals(Object)}, ???? * record classes must further participate in the invariant that when ???? * a record instance is "copied" by passing the result of the record component ???? * accessor methods to the canonical constructor, the resulting copy is ???? * equal to the original instance. ???? * ???? * @implNote ???? * The implicitly provided implementation returns {@code true} if and ???? * only if the argument is an instance of the same record type as this object, ???? * and each component of this record is equal to the corresponding component ???? * of the argument, according to {@link Object#equals(Object)} for components ???? * whose types are reference types, and {@code ==} for components whose ???? * types are primitive types. ???? * ???? * @see Object#equals(Object) ???? * ???? * @param?? obj?? the reference object with which to compare. ???? * @return? {@code true} if this object is the same as the obj ???? *????????? argument; {@code false} otherwise. ???? */ ??? @Override ??? public abstract boolean equals(Object obj); ??? /** ???? * {@inheritDoc} ???? * ???? * @implNote ???? * The implicitly provided implementation returns a hash code value derived ???? * by combining the hash code value for all the components, according to ???? * {@link Object#hashCode()} for components whose types are reference types, ???? * or the primitive wrapper hash code for components whose types are primitive ???? * types. ???? * ???? * @see???? Object#hashCode() ???? * ???? * @return? a hash code value for this object. ???? */ ??? @Override ??? public abstract int hashCode(); ??? /** ???? * {@inheritDoc} ???? * ???? * @implNote ???? * The implicitly provided implementation returns a string that is derived ???? * from the name of the record class and the names and string representations ???? * of all the components, according to {@link Object#toString()} for components ???? * whose types are reference types, and the primitive wrapper {@code toString} ???? * method for components whose types are primitive types. ???? * ???? * @see???? Object#toString() () ???? * ???? * @return? a string representation of the object. ???? */ ??? @Override ??? public abstract String toString(); } On 8/15/2019 1:06 PM, Brian Goetz wrote: > > Draft spec, please comment. > > /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights > reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE > HEADER. * * This code is free software; you can redistribute it and/or > modify it * under the terms of the GNU General Public License version > 2 only, as * published by the Free Software Foundation. Oracle > designates this * particular file as subject to the "Classpath" > exception as provided * by Oracle in the LICENSE file that accompanied > this code. * * This code is distributed in the hope that it will be > useful, but WITHOUT * ANY WARRANTY; without even the implied warranty > of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU > General Public License * version 2 for more details (a copy is > included in the LICENSE file that * accompanied this code). * * You > should have received a copy of the GNU General Public License version > * 2 along with this work; if not, write to the Free Software > Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 > USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA > 94065 USA * or visit www.oracle.com if you need additional information > or have any * questions. */ package java.lang; > > /** * This is the common base class of all Java language record > classes. * *

More information about records, including descriptions > of the * implicitly declared methods synthesized by the compiler, can > be * found in section 8.10 of * The Java™Language > Specification. * *

A record classis a shallowly > immutable, transparent carrier for * a fixed set of values, called the > record components. The Java™* language provides concise > syntax for declaring record classes, whereby the * record components > are declared in the record header. The list of record * components > declared in the record header form the record descriptor. * * >

A record class has the following mandated members: a public > canonical * constructor, whose descriptor is the same as the > record descriptor; * a private static field corresponding to each > component, whose name and * type are the same as that of the > component; a public accessor method * corresponding to each component, > whose name and return type are the same as * that of the component. If > not explicitly declared in the body of the record, * implicit > implementations for these members are provided. * *

The implicit > declaration of the canonical constructor initializes the * component > fields from the corresponding constructor arguments. The implicit * > declaration of the accessor methods returns the value of the > corresponding * component field. The implicit declaration of the > {@link Object#equals(Object)}, * {@link Object#hashCode()}, and {@link > Object#toString()} methods are derived * from all of the component > fields. * *

The primary reasons to provide an explicit declaration > for the * canonical constructor or accessor methods are to validate > constructor * arguments, perform defensive copies on mutable > components, or normalize groups * of components (such as reducing a > rational number to lowest terms.) If any * of these are provided > explicitly. * *

For all record classes, the following invariant > must hold: if a record R's * components are {@code c1, c2, ... cn}, > then if a record instance is copied * as follows: *

* R copy = 
> new R(r.c1(), r.c2(), ..., r.cn()); * 
* then it must be the case > that {@code r.equals(copy)}. * * @jls 8.10 * @since 14 */ public > abstract class Record { > /** * Indicates whether some other object is "equal to" this one. In > addition * to the general contract of {@link Object#equals(Object)}, * > record classes must further participate in the invariant that when * a > record instance is "copied" by passing the result of the record > component * accessor methods to the canonical constructor, the > resulting copy is * equal to the original instance. * * @implNote * > The implicitly provided implementation returns {@code true} if and * > only if the argument is an instance of the same record type as this > object, * and each component of this record is equal to the > corresponding component * of the argument, according to {@link > Object#equals(Object)} for components * whose types are reference > types, and {@code ==} for components whose * types are primitive > types. * * @see Object#equals(Object) * * @param obj the reference > object with which to compare. * @return {@code true} if this object is > the same as the obj * argument; {@code false} otherwise. */ @Override > public abstract boolean equals(Object obj); > > /** * {@inheritDoc} * * @implNote * The implicitly provided > implementation returns a hash code value derived * by combining the > hash code value for all the components, according to * {@link > Object#hashCode()} for components whose types are reference types, * > or the primitive wrapper hash code for components whose types are > primitive * types. * * @see Object#hashCode() * * @return a hash code > value for this object. */ @Override public abstract int hashCode(); > > /** * {@inheritDoc} * * @implNote * The implicitly provided > implementation returns a string that is derived * from the name of the > record class and the names and string representations * of all the > components, according to {@link Object#toString()} for components * > whose types are reference types, and the primitive wrapper {@code > toString} * method for components whose types are primitive types. * * > @see Object#toString() () * * @return a string representation of the > object. */ @Override public abstract String toString(); > } From brian.goetz at oracle.com Thu Aug 15 17:38:23 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 13:38:23 -0400 Subject: Refinements for sealed types Message-ID: As Gavin has worked through the spec for sealed types, it has shone a spotlight on one of the messy parts -- implicitly sealed types. I've got an alternate way to stack this that I think is simpler and still meets the goals. First, the goals (which in most cases align, but in some cases conflict with each other): Unsealed concrete leaves should not be the default. If we follow the default strategy that a class declaration gets only the flags that are declared explicitly (which is generally a good default), it is quite likely that many subtypes of sealed types will be extensible, when that was not the intent or understanding of the author. For example, in a hierarchy like the following: sealed interface X permits A, B { } class A implements X { } class B implements X { } it is highly likely to be a source of mistakes that A and B are neither sealed nor final, even though they are co-declared with the sealed interface. The author may well assume that they are in control of all the implementations of X methods, when in fact anyone can subclass A and override those methods. Avoiding excessive ceremony. If the user had to declare every concrete subtype as final, this may well be seen as excess ceremony. (This is the same reason we allow the permits clause to be inferred.) In the current design, we took the following path: - Subtypes of sealed types are implicitly sealed, unless marked non-sealed. - We infer a permits clause when it is omitted, which is possibly empty (in which case the type is effectively final.) These are reasonable defaults, both from avoiding the safety question of accidental extensibility, and reducing ceremony, but they interact in some uncomfortable ways. For example, under these rules, its possible to have an explicit "permits" clause without saying "sealed", which is a little strange. And it is possible to infer both sealing and the permits list, which might exceed our nontransparency comfort level. So, let me propose a simplification: - A concrete subtype A of a sealed type X, which has no permits clause, no known subtypes, and is not marked non-sealed, is implicitly sealed (with an empty permits clause). - Any other subtype of a sealed type must either have a "sealed" modifier, or a "non-sealed" modifier. - Any type with a permits list must have a sealed modifier. Rationale: This still manages to avoid the "accidental extension" problem, and addresses both the extension and the ceremony problem where they are both most severe -- at the leaves. Abstract types (which are generally fewer in number than leaves) err on the side of explicitness. So the above hierarchy could be written as above, and A/B would be implicitly final, as desired. If A or B wanted to permit subtypes, they would need to be explicitly marked non-sealed (to reopen them), or explicitly sealed (with or without an explicit permits list.) I think this stays within the spirit of the goals, but with a little less magic / complexity. From guy.steele at oracle.com Thu Aug 15 17:39:58 2019 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 15 Aug 2019 13:39:58 -0400 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <49C15995-1FA8-4DBD-B0E9-3D1B5B29D51D@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> <35C23E6E-3F01-4F63-B01A-9E6C3469B749@oracle.com> <7E891CD8-BBD8-49FB-83DA-3CB7653EF08B@oracle.com> <49C15995-1FA8-4DBD-B0E9-3D1B5B29D51D@oracle.com> Message-ID: <2E8D42D3-647C-47C7-9514-8CFBD10D9FBD@oracle.com> > On Aug 15, 2019, at 11:19 AM, Jim Laskey wrote: > > Assuming the proposal is (keep last white space), I see the light but I see the dark here as well (and maybe not enough here to switch horses.) > > String hexData = """ > 000102030405060708090a0b0c0d0e0f101112131415 \ > 161718191a1b1c1d1e1f202122232425262728292a2b2c \ > 2d2e2f303132333435363738393a3b3c3d3e3f40 \ > 4142434445464748494a4b4c4d4e4f505152535455 \ > """; > > This sort of pattern is high maintenance; aligning backslashes. A good IDE will help you align the backslashes. EMACS C mode certainly does. (Note how carefully I have finessed the question of whether EMACS actually constitutes a ?good IDE?! :-) But I note that this style of aligned backslashes is used in the C world only in situations where it really doesn?t matter how much whitespace there is where the line is broken. This suggests the following tweak to John?s proposal: replace < LWS* \ LT LWS* LWS > and likewise < LWS LWS* \ LT LWS* > with a single space, and otherwise simply delete < \ LT >. (If this isn?t what you want, then again, you can use \s to delimit what you want kept.) From brian.goetz at oracle.com Thu Aug 15 18:35:16 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 14:35:16 -0400 Subject: Varargs records Message-ID: As I was writing some tests for records, it occurs to me that we never explicitly discussed whether we should support _varargs_ records, such as: ??? record StringBunch(String... strings) { } The translation would be straightforward: ?- The type of the strings field is String[] ?- The return type of the strings accessor is String[] ?- The canonical constructor is of descriptor (String...) rather than (String[]), allowing varargs invocation of the constructor. ?- There would be the usual restriction that there can be only one varargs argument, and it must be last. So this seems feasible.? Is this something we want? From kevinb at google.com Thu Aug 15 18:37:15 2019 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 15 Aug 2019 11:37:15 -0700 Subject: Varargs records In-Reply-To: References: Message-ID: I believe anyone would expect this to work as described, certainly. On Thu, Aug 15, 2019 at 11:35 AM Brian Goetz wrote: > As I was writing some tests for records, it occurs to me that we never > explicitly discussed whether we should support _varargs_ records, such as: > > record StringBunch(String... strings) { } > > The translation would be straightforward: > - The type of the strings field is String[] > - The return type of the strings accessor is String[] > - The canonical constructor is of descriptor (String...) rather than > (String[]), allowing varargs invocation of the constructor. > - There would be the usual restriction that there can be only one > varargs argument, and it must be last. > > So this seems feasible. Is this something we want? > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From alex.buckley at oracle.com Thu Aug 15 19:14:04 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Thu, 15 Aug 2019 12:14:04 -0700 Subject: Draft specification for java.lang.Record In-Reply-To: <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> Message-ID: <4b51d90d-ddb8-5a6c-1e60-261d0a83923d@oracle.com> I am reading this javadoc from the POV of someone in 2034 (15 years hence, like we are 15 years from Enum) who doesn't know anything about Amber. On 8/15/2019 10:34 AM, Brian Goetz wrote: > /** > ?* This is the common base class of all Java language record classes. I know this borrows from Enum, but "base class" is a terrible un-Java phrase there and it's terrible here. "This is the common superclass of all record classes in the Java language." > ?* > ?*

More information about records, including descriptions of the > ?* implicitly declared methods synthesized by the compiler, can be > ?* found in section 8.10 of > ?* The Java™ Language Specification. Too much too soon; drop. > ?* > ?*

A record class is a shallowly immutable, transparent > carrier for > ?* a fixed set of values, called the record components.? The > Java™ > ?* language provides concise syntax for declaring record classes, > whereby the > ?* record components are declared in the record header.? The list of record > ?* components declared in the record header form the record > descriptor. Are classes immutable, or objects? I think "shallowly immutable" belongs in a paragraph about "records" or "record instances" (not "record classes") that is not yet written, but should appear just before "For all record classes, ..." > ?* > ?*

A record class has the following mandated members: a public > canonical > ?* constructor, whose descriptor is the same as the record descriptor; "A record class has a public canonical constructor, whose signature is the same ...;" > ?* a private ... field corresponding to each component, whose name and > ?* type are the same as that of the component; a public accessor method > ?* corresponding to each component, whose name and return type are the > same as > ?* that of the component.? If not explicitly declared in the body of > the record, Prefer "; and a public _no-args_ accessor method corresponding ... Any or all of these elements may be declared explicitly; if one is not declared explicitly, then it is provided implicitly." > ?* implicit implementations for these members are provided. > ?* > ?*

The implicit declaration of the canonical constructor initializes the > ?* component fields from the corresponding constructor arguments. The > implicit > ?* declaration of the accessor methods returns the value of the > corresponding > ?* component field.? The implicit declaration of the {@link > Object#equals(Object)}, > ?* {@link Object#hashCode()}, and {@link Object#toString()} methods are > derived > ?* from all of the component fields. > ?* > ?*

The primary reasons to provide an explicit declaration for the > ?* canonical constructor or accessor methods are to validate constructor > ?* arguments, perform defensive copies on mutable components, or > normalize groups > ?* of components (such as reducing a rational number to lowest terms.) > If any > ?* of these are provided explicitly. Can't grok "mutable components". Do you mean the (mutable) constructor arguments which correspond to components? Prefer ", or cross-check the values of different components." instead of normalizing and reducing which again is hard to follow. Expecting: "An instance of a record class is _shallowly immutable_, which means ..." > ?* > ?*

For all record classes, the following invariant must hold: if a > record R's > ?* components are {@code c1, c2, ... cn}, then if a record instance is > copied > ?* as follows: Calling `R` a "record" is obtuse since the invariant is about "record classes" and this whole spec outlines a "record class". There should be two kinds of entity -- "record class" and "record instance", or "record class" and "record" -- not three. Also, "Given a non-null instance `r` of record class `R`, whose components are ..., then if `r` is copied in the following way:" > ?*

>  ?*???? R copy = new R(r.c1(), r.c2(), ..., r.cn());
>  ?* 
> ?* then it must be the case that {@code r.equals(copy)}. A name like "copy equality" or "by parts equality" or "component equality" would be helpful for this property, both for the spec of equals and for developers' general knowledge. Cloning: if a record class was to implement Cloneable, then the inherited implementation of Object::clone would not preserve copy equality (because, yes, cloning is not the same as copying). Recommend not implementing Cloneable? Alex From alex.buckley at oracle.com Thu Aug 15 19:39:07 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Thu, 15 Aug 2019 12:39:07 -0700 Subject: Refinements for sealed types In-Reply-To: References: Message-ID: On 8/15/2019 10:38 AM, Brian Goetz wrote: > ??? sealed interface X permits A, B { } > ??? class A implements X { } > ??? class B implements X { } > > In the current design, we took the following path: > > ?- Subtypes of sealed types are implicitly sealed, unless marked > non-sealed. > ?- We infer a permits clause when it is omitted, which is possibly > empty (in which case the type is effectively final.) So, A is implicitly sealed, but (IIRC) its lack of `permits` means that any class which is in the same compilation unit as A and which says `... extends A` is a permitted subtype. And, you are saying that it's not reasonable for A's author to have to oversee the whole compilation unit all the time, just in case some permitted subtype is lurking around with a `non-sealed` modifier that lets the X hierarchy be polluted yet further. > So, let me propose a simplification: > > ?- A concrete subtype A of a sealed type X, which has no permits > clause, no known subtypes, and is not marked non-sealed, is implicitly > sealed (with an empty permits clause). Sounds good -- and where "implicitly sealed (with an empty permits clause)" === "implicitly final", right? > ?- Any other subtype of a sealed type must either have a "sealed" > modifier, or a "non-sealed" modifier. > ?- Any type with a permits list must have a sealed modifier. Alex From alex.buckley at oracle.com Thu Aug 15 19:54:37 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Thu, 15 Aug 2019 12:54:37 -0700 Subject: Draft specification for java.lang.Record In-Reply-To: <2a950d59-3bd6-5837-e2f6-e2ab50856389@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> <4b51d90d-ddb8-5a6c-1e60-261d0a83923d@oracle.com> <2a950d59-3bd6-5837-e2f6-e2ab50856389@oracle.com> Message-ID: <4ef2786a-dcbf-a494-b98f-aaf745361391@oracle.com> On 8/15/2019 12:18 PM, Brian Goetz wrote: >> Cloning: if a record class was to implement Cloneable, then the >> inherited implementation of Object::clone would not preserve copy >> equality (because, yes, cloning is not the same as copying). Recommend >> not implementing Cloneable? > > We have an opportunity to do any of the following: > > ?- Prohibit cloning by making Record::clone final; > ?- Be clone-agnostic by saying nothing; > ?- Promote cloning by making Record implement Cloneable, and having an > implementation of clone() either in Record.java, or having the compiler > generate it according to the obvious component-copying formula. j.l.Record's copy-equality property suggests that copying a record instance should be done neither by shallow-copy nor by deep-copy. Bringing Cloneable's shallow-copy into the picture muddies things up. My view isn't worth much here, but prohibition of cloning looks good to me. Alex From kevinb at google.com Thu Aug 15 19:54:19 2019 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 15 Aug 2019 12:54:19 -0700 Subject: Draft specification for java.lang.Record In-Reply-To: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: On Thu, Aug 15, 2019 at 10:07 AM Brian Goetz wrote: > Draft spec, please comment. > > > /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package java.lang; > /** * This is the common base class of all Java language record classes. > > Well, and *only* record classes -- it can't be extended manually. It seems useful to understand both necessity and sufficiency, though I notice Enum also doesn't say this. > * *

More information about records, including descriptions of the > > To the extent that the bare term "record" (without "class") has meaning, that is still something that is always declared by a "record class" anyway, so might it be clearer to always say "record classes"? Just a thought. > * implicitly declared methods synthesized by the compiler, can be * found in section 8.10 of * The Java™ Language Specification. * *

A record class is a shallowly immutable, transparent carrier for > > Typical readers may wonder what "transparent" means here. (They may also not understand shallow immutability, but that they can probably look up.) > * a fixed set of values, called the record components. The Java™ * language provides concise syntax for declaring record classes, whereby the * record components are declared in the record header. The list of record * components declared in the record header form the record descriptor. * *

A record class has the following mandated members: a public canonical * constructor, whose descriptor is the same as the record descriptor; * a private static field corresponding to each component, whose name and * type are the same as that of the component; a public accessor method * corresponding to each component, whose name and return type are the same as * that of the component. If not explicitly declared in the body of the record, > > Even if "record" is used in some contexts this still seems to want to be "record class"? > * implicit implementations for these members are provided. * *

The implicit declaration of the canonical constructor initializes the * component fields from the corresponding constructor arguments. The implicit * declaration of the accessor methods returns the value of the corresponding * component field. The implicit declaration of the {@link Object#equals(Object)}, * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived * from all of the component fields. > > That may be confusing, but if you add "... as specified below" or something like that it's all good. *

The primary reasons to provide an explicit declaration for the * canonical constructor or accessor methods are to validate constructor * arguments, perform defensive copies on mutable components, or normalize groups * of components (such as reducing a rational number to lowest terms.) > > Is this intentionally excluding normalizing a single component? > If any * of these are provided explicitly. * *

For all record classes, the following invariant must hold: if a record R's > > "a record class R's"? More importantly, was the "if any of these are provided explicitly" meant to be part of this new paragraph? I'd like the part about the invariant to explain both that it does always hold by default AND that if you choose to customize anything that's when you become responsible for upholding it. * components are {@code c1, c2, ... cn}, then if a record instance is copied * as follows: *

 *     R copy = new R(r.c1(), r.c2(), ...,
r.cn()); * 
* then it must be the case that {@code r.equals(copy)}. * * @jls 8.10 * @since 14 */public abstract class Record { > /** * Indicates whether some other object is "equal to" this one. In addition > > We could improve on the superclass wording, like "Indicates whether {@code obj} is a record with equal corresponding values to this one." * to the general contract of {@link Object#equals(Object)}, * record classes must further participate in the invariant that when * a record instance is "copied" by passing the result of the record component * accessor methods to the canonical constructor, the resulting copy is * equal to the original instance. > > Finding this sentence awfully unwieldy. Could some of the ideas from this formulation help? "As illustrated in the class documentation, this method must return {@code true} if {@code obj} is a simple componentwise copy of this record (it may, of course, return {@code true} in other cases as well)." Sure, "simple componentwise copy" is not self-defining, but that's why there's an illustration up top. * @implNote * The implicitly provided implementation returns {@code true} if and > > Perhaps start by saying "Although this method is abstract, record classes do not need to implement it explicitly" and *then* explain what the implicit impl does? * only if the argument is an instance of the same record type as this object, * and each component of this record is equal to the corresponding component * of the argument, according to {@link Object#equals(Object)} for components * whose types are reference types, and {@code ==} for components whose * types are primitive types. > > The floating-point issue jumped to my mind as well. But we really don't want to have to call out such a fine-grained special case, so something like "(boxing primitive values if necessary)" could do the trick? The main danger of this behavior is arrays; is it worth giving that a callout here? * @see Object#equals(Object) * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. > > It is strange to have clear method docs and then a @return clause that makes things muddy again. Maybe it should reference "as described more fully in the method documentation"? (This is why @return clauses shouldn't be mandatory; they very often subtract value in just this way, but nothing we can do about that here...) Is it worth a reminder here that if you provide any alternate behavior for this method you must do so for hashCode as well? imho, you should even be encouraged to do so for toString() too (or you will get confusing "expected but was test failures, not that we want to say all that here). */ @Override public abstract boolean equals(Object obj); > > /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a hash code value derived * by combining the hash code value for all the components, according to * {@link Object#hashCode()} for components whose types are reference types, * or the primitive wrapper hash code for components whose types are primitive * types. > > It's unclear to me why it's even worth saying any of this. It seems like it's trying to communicate the fact that it's neither going to randomly ignore some components, nor try to compute a hash code for a component via some weird other means. But then, since it's not specifying *how* exactly it will *combine* the values, it's not really guaranteeing it doesn't do those weird things anyway. I tend to think this method doesn't need to say anything at all. Perhaps just to say that there is almost no reason to provide your own implementation unless you are doing so for equals() (in which case you must). * * @see Object#hashCode() * * @return a hash code value for this object. */ @Override public abstract int hashCode(); > > /** * {@inheritDoc} * * @implNote * The implicitly provided implementation returns a string that is derived * from the name of the record class and the names and string representations * of all the components, according to {@link Object#toString()} for components * whose types are reference types, and the primitive wrapper {@code toString} * method for components whose types are primitive types. * * @see Object#toString() () * * @return a string representation of the object. */ @Override public abstract String toString(); > } > > Perhaps mention that the output does not incorporate the component names. (off-topic: it would be nice if there was a final toSelfDescribingString() method I could delegate my toString() to if I wanted that. It's what most classes by far would want, I think.) -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Aug 15 20:02:35 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 16:02:35 -0400 Subject: Refinements for sealed types In-Reply-To: References: Message-ID: <0d1bd204-bdac-23fe-d115-6549977328b5@oracle.com> > So, A is implicitly sealed, but (IIRC) its lack of `permits` means > that any class which is in the same compilation unit as A and which > says `... extends A` is a permitted subtype. Currently, yes. > And, you are saying that it's not reasonable for A's author to have to > oversee the whole compilation unit all the time, just in case some > permitted subtype is lurking around with a `non-sealed` modifier that > lets the X hierarchy be polluted yet further. It's more about reading than writing.? Having both the sealed-ness be implicit, and the permits list be implicit, puts a lot of strain on the _reader_.? (Yes, Javadoc will spell it out, but source readers may not.) > >> So, let me propose a simplification: >> >> ??- A concrete subtype A of a sealed type X, which has no permits >> clause, no known subtypes, and is not marked non-sealed, is >> implicitly sealed (with an empty permits clause). > > Sounds good -- and where "implicitly sealed (with an empty permits > clause)" === "implicitly final", right? implicitly effectively final, at least.? Whether it is _actually_ ACC_FINAL is a separate matter. From vicente.romero at oracle.com Thu Aug 15 20:29:13 2019 From: vicente.romero at oracle.com (Vicente Romero) Date: Thu, 15 Aug 2019 16:29:13 -0400 Subject: Refinements for sealed types In-Reply-To: <0d1bd204-bdac-23fe-d115-6549977328b5@oracle.com> References: <0d1bd204-bdac-23fe-d115-6549977328b5@oracle.com> Message-ID: <381f0ead-9465-903d-cb81-87db5de19d47@oracle.com> On 8/15/19 4:02 PM, Brian Goetz wrote: > > >> So, A is implicitly sealed, but (IIRC) its lack of `permits` means >> that any class which is in the same compilation unit as A and which >> says `... extends A` is a permitted subtype. > > Currently, yes. > >> And, you are saying that it's not reasonable for A's author to have >> to oversee the whole compilation unit all the time, just in case some >> permitted subtype is lurking around with a `non-sealed` modifier that >> lets the X hierarchy be polluted yet further. > It's more about reading than writing.? Having both the sealed-ness be > implicit, and the permits list be implicit, puts a lot of strain on > the _reader_.? (Yes, Javadoc will spell it out, but source readers may > not.) > >> >>> So, let me propose a simplification: >>> >>> ??- A concrete subtype A of a sealed type X, which has no permits >>> clause, no known subtypes, and is not marked non-sealed, is >>> implicitly sealed (with an empty permits clause). >> >> Sounds good -- and where "implicitly sealed (with an empty permits >> clause)" === "implicitly final", right? > > implicitly effectively final, at least.? Whether it is _actually_ > ACC_FINAL is a separate matter. > well right now all sealed types have the ACC_FINAL set, so would implicit sealed classes I think From brian.goetz at oracle.com Thu Aug 15 20:47:40 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 16:47:40 -0400 Subject: Draft specification for java.lang.Record In-Reply-To: References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: > /** * This is the common base class of all Java language record > classes. > > Well, and /only/?record classes -- it can't be extended manually. It > seems useful to understand both necessity and sufficiency, though I > notice Enum also doesn't say this. Right, that's something for the JLS to say: "it is a compile-time error ..." > * *

More information about records, including descriptions of the > > To the extent that the?bare?term "record" (without "class") has > meaning, that is still something that is always declared by a "record > class" anyway, so might it be clearer to always say "record classes"? > Just a thought. Yes, I think its better to go this way than the other way.? We want to remind people that records are, first and foremost, classes. > * implicitly declared methods synthesized by the compiler, can be > * found in section 8.10 of * The Java™Language > Specification. * *

A record classis a shallowly > immutable, transparent carrier for > > Typical readers may wonder what "transparent" means here. (They may > also not understand shallow immutability, but that they can probably > look up.) What's a better way to say this??? This goes back to "the state, the whole state, and nothing but the state"; a record shouldn't hide much from its clients. > > > *

The primary reasons to provide an explicit declaration for > the * canonical constructor or accessor methods are to validate > constructor * arguments, perform defensive copies on mutable > components, or normalize groups * of components (such as reducing > a rational number to lowest terms.) > > Is this intentionally excluding normalizing a single component? No, will adjust. > > The floating-point issue jumped to my mind as well. But we really > don't want to have to call out such a fine-grained special case, so > something like "(boxing primitive values if necessary)" could do the > trick? Semantically, that's right; I worried about Java developers and their allergy to boxing, and then tomorrow we'll contend with "records are slow, don't use them." > The main danger of this behavior is arrays; is it worth giving that a > callout here? Yes, probably. > */ > > @Override public abstract boolean equals(Object obj); > > /** * {@inheritDoc} * * @implNote * The implicitly provided > implementation returns a hash code value derived * by combining > the hash code value for all the components, according to * {@link > Object#hashCode()} for components whose types are reference types, > * or the primitive wrapper hash code for components whose types > are primitive * types. > > It's unclear to me why it's even worth saying any of this. It seems > like it's trying to communicate the fact that it's neither going to > randomly ignore some components, nor try to compute a hash code for a > component via some weird other means. But then, since it's not > specifying *how* exactly it will /combine/ the values, it's not really > guaranteeing it doesn't do those weird things anyway. It is mostly an in-terrorum warning not to try to do fancy stuff (constrained by the desire to not overspecify the algorithm.) People will think its clever to redefine equals/hashCode to only consider a subset of fields, and they'll be surprised why frameworks then treat their records weirdly. > > Perhaps mention that the output does not incorporate the component names. But it does: jshell> record R(int x) { } |? created class R jshell> new R(3) $2 ==> R[x=3] From kevinb at google.com Thu Aug 15 21:26:51 2019 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 15 Aug 2019 14:26:51 -0700 Subject: Draft specification for java.lang.Record In-Reply-To: References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: On Thu, Aug 15, 2019 at 1:47 PM Brian Goetz wrote: > /** * This is the common base class of all Java language record classes. >> >> Well, and *only* record classes -- it can't be extended manually. It > seems useful to understand both necessity and sufficiency, though I notice > Enum also doesn't say this. > > Right, that's something for the JLS to say: "it is a compile-time error > ..." > Yeah, I just felt it's useful information to any person perusing the class docs as well. *

A record class is a shallowly immutable, transparent carrier for >> >> Typical readers may wonder what "transparent" means here. (They may also > not understand shallow immutability, but that they can probably look up.) > > What's a better way to say this? This goes back to "the state, the whole > state, and nothing but the state"; a record shouldn't hide much from its > clients. > I confess I don't know the better way either. Maybe an additional sentence saying "Transparency means that...". The floating-point issue jumped to my mind as well. But >> we really don't want to have to call out such a fine-grained >> special case, so something like "(boxing primitive values if >> necessary)" could do the trick? >> >> Semantically, that's right; I worried about Java developers and their > allergy to boxing, and then tomorrow we'll contend with "records are slow, > don't use them." > Well, *someone *would think that. But I don't know, insert "*as if by*" in the right place and maybe we're fine. * @implNote * The implicitly provided implementation returns a hash code value derived * by combining the hash code value for all the components, according to * {@link Object#hashCode()} for components whose types are reference types, * or the primitive wrapper hash code for components whose types are primitive * types. >> >> It's unclear to me why it's even worth saying any of this. It seems like > it's trying to communicate the fact that it's neither going to randomly > ignore some components, nor try to compute a hash code for a component via > some weird other means. But then, since it's not specifying *how* exactly > it will *combine* the values, it's not really guaranteeing it doesn't do > those weird things anyway. > > It is mostly an in-terrorum warning not to try to do fancy stuff > (constrained by the desire to not overspecify the algorithm.) People will > think its clever to redefine equals/hashCode to only consider a subset of > fields, and they'll be surprised why frameworks then treat their records > weirdly. > Hmm, do you want to say something more direct about that then? Perhaps mention that the output does not incorporate the component names. > > But it does: > Perhaps mention that the output does incorporate the component names. :-) Cool. Is it worth calling this out? Note: I don't want to specify the exact form; it's just that the reader's gotta decide whether to accept the default implementation or not somehow. Perhaps Record.toString() and Enum.toString() ought to carry a warning that their output may change when a component/field is renamed. We generally want to assume (in the absence of reflection) that renaming something along with all its references is strictly behavior-preserving. From amaembo at gmail.com Fri Aug 16 00:57:41 2019 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 16 Aug 2019 07:57:41 +0700 Subject: Varargs records In-Reply-To: References: Message-ID: Allowing this would encourage people to use arrays in records. Unfortunately, according to current spec they will have broken equals/hashCode. Especially in vararg mode: you will always have a new array, so no record will be equal to another one unless explicit array is used instead of vararg. Is this what we want? With best regards, Tagir Valeev. ??, 16 ???. 2019 ?., 1:35 Brian Goetz : > As I was writing some tests for records, it occurs to me that we never > explicitly discussed whether we should support _varargs_ records, such as: > > record StringBunch(String... strings) { } > > The translation would be straightforward: > - The type of the strings field is String[] > - The return type of the strings accessor is String[] > - The canonical constructor is of descriptor (String...) rather than > (String[]), allowing varargs invocation of the constructor. > - There would be the usual restriction that there can be only one > varargs argument, and it must be last. > > So this seems feasible. Is this something we want? > From brian.goetz at oracle.com Fri Aug 16 01:14:07 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Aug 2019 21:14:07 -0400 Subject: Varargs records In-Reply-To: References: Message-ID: <7e6d066d-231f-f274-b428-80636a5947e0@oracle.com> That's not exactly true; it's not broken, but it's possibly not what people expect.? The implicit implementations of ctor/accessor/and equals work together; the ctor copies the reference, the accessor dispenses the reference, and equals compares by reference equality.? So you get the invariants you expect, but you end up exposing the mutability of the array (until we have frozen arrays, which I desperately want, for this very reason.) If you wanted to make the record more bulletproof, you'd have to: ??? record R(STUFF, String... strings) { ??????? R { strings = strings.clone(); } ??????? public String[] strings() { return strings.clone(); } ??????? public boolean equals(Object o) { ??????????? return o instanceof R r && ... STUFF ... && Arrays.equals(strings, r.strings); ??????? } ??? } which is kind of messy but not ... awful. But either way, people can use arrays in records -- this just makes it more likely that they will. On 8/15/2019 8:57 PM, Tagir Valeev wrote: > Allowing this would encourage people to use arrays in records. > Unfortunately, according to current spec they will have broken > equals/hashCode. Especially in vararg mode: you will always have a new > array, so no record will be equal to another one unless explicit array > is used instead of vararg. Is this what we want? > > With best regards, > Tagir Valeev. > > ??, 16 ???. 2019 ?., 1:35 Brian Goetz >: > > As I was writing some tests for records, it occurs to me that we > never > explicitly discussed whether we should support _varargs_ records, > such as: > > ???? record StringBunch(String... strings) { } > > The translation would be straightforward: > ??- The type of the strings field is String[] > ??- The return type of the strings accessor is String[] > ??- The canonical constructor is of descriptor (String...) rather > than > (String[]), allowing varargs invocation of the constructor. > ??- There would be the usual restriction that there can be only one > varargs argument, and it must be last. > > So this seems feasible.? Is this something we want? > From forax at univ-mlv.fr Sat Aug 17 11:05:11 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 17 Aug 2019 13:05:11 +0200 (CEST) Subject: Draft specification for java.lang.Record In-Reply-To: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> Message-ID: <218857967.503581.1566039911517.JavaMail.zimbra@u-pem.fr> Hi all, why Record is a class (an abstract class) and not an interface ? It's not like j.l.Enum which has two fields and several method implementations, Record just strengthen the contract of Object like a Set strengthen the contract of Collection, so it seems more like an interface to me. Being an interface also means that we will be able to mix an inline type and a record, an inline record?, for free, and more generally Record being an interface is less disruptive than being a class when you want to retrofit a class to be a record. R?mi > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Jeudi 15 Ao?t 2019 19:06:02 > Objet: Draft specification for java.lang.Record > Draft spec, please comment. > /* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO > NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is > free software; you can redistribute it and/or modify it * under the terms of > the GNU General Public License version 2 only, as * published by the Free > Software Foundation. Oracle designates this * particular file as subject to > the "Classpath" exception as provided * by Oracle in the LICENSE file that > accompanied this code. * * This code is distributed in the hope that it will be > useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General > Public License * version 2 for more details (a copy is included in the LICENSE > file that * accompanied this code). * * You should have received a copy of the > GNU General Public License version * 2 along with this work; if not, write to > the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA > 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, > CA 94065 USA * or visit [ http://www.oracle.com/ | www.oracle.com ] if you need > additional information or have any * questions. */ package java.lang; /** * > This is the common base class of all Java language record classes. * *

More > information about records, including descriptions of the * implicitly declared > methods synthesized by the compiler, can be * found in section 8.10 of * > The Java ™ Language Specification . * *

A record class > is a shallowly immutable, transparent carrier for * a fixed set of > values, called the record components . The Java ™ * language > provides concise syntax for declaring record classes, whereby the * record > components are declared in the record header. The list of record * components > declared in the record header form the record descriptor . * *

A > record class has the following mandated members: a public canonical * > constructor , whose descriptor is the same as the record descriptor; * a > private static field corresponding to each component, whose name and * type are > the same as that of the component; a public accessor method * corresponding to > each component, whose name and return type are the same as * that of the > component. If not explicitly declared in the body of the record, * implicit > implementations for these members are provided. * *

The implicit > declaration of the canonical constructor initializes the * component fields > from the corresponding constructor arguments. The implicit * declaration of > the accessor methods returns the value of the corresponding * component field. > The implicit declaration of the { @link Object#equals(Object)}, * { @link > Object#hashCode()}, and { @link Object#toString()} methods are derived * from > all of the component fields. * *

The primary reasons to provide an explicit > declaration for the * canonical constructor or accessor methods are to validate > constructor * arguments, perform defensive copies on mutable components, or > normalize groups * of components (such as reducing a rational number to lowest > terms.) If any * of these are provided explicitly. * *

For all record > classes, the following invariant must hold: if a record R's * components are { > @code c1, c2, ... cn}, then if a record instance is copied * as follows: * >

 *     R copy = new R(r.c1(), r.c2(), ..., r.cn()); * 
* then it > must be the case that { @code r.equals(copy)}. * * @jls 8.10 * @since 14 */ > public abstract class Record { /** * Indicates whether some other object is > "equal to" this one. In addition * to the general contract of { @link > Object#equals(Object)}, * record classes must further participate in the > invariant that when * a record instance is "copied" by passing the result of > the record component * accessor methods to the canonical constructor, the > resulting copy is * equal to the original instance. * * @implNote * The > implicitly provided implementation returns { @code true} if and * only if the > argument is an instance of the same record type as this object, * and each > component of this record is equal to the corresponding component * of the > argument, according to { @link Object#equals(Object)} for components * whose > types are reference types, and { @code ==} for components whose * types are > primitive types. * * @see Object#equals(Object) * * @param obj the reference > object with which to compare. * @return { @code true} if this object is the > same as the obj * argument; { @code false} otherwise. */ @Override > public abstract boolean equals(Object obj); /** * { @inheritDoc } * * @implNote > * The implicitly provided implementation returns a hash code value derived * by > combining the hash code value for all the components, according to * { @link > Object#hashCode()} for components whose types are reference types, * or the > primitive wrapper hash code for components whose types are primitive * types. * > * @see Object#hashCode() * * @return a hash code value for this object. */ > @Override public abstract int hashCode(); /** * { @inheritDoc } * * @implNote * > The implicitly provided implementation returns a string that is derived * from > the name of the record class and the names and string representations * of all > the components, according to { @link Object#toString()} for components * whose > types are reference types, and the primitive wrapper { @code toString} * method > for components whose types are primitive types. * * @see Object#toString() () * > * @return a string representation of the object. */ @Override public abstract > String toString(); > } From forax at univ-mlv.fr Sat Aug 17 11:21:24 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 17 Aug 2019 13:21:24 +0200 (CEST) Subject: Refinements for sealed types In-Reply-To: <0d1bd204-bdac-23fe-d115-6549977328b5@oracle.com> References: <0d1bd204-bdac-23fe-d115-6549977328b5@oracle.com> Message-ID: <652149186.504536.1566040884085.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Alex Buckley" , "amber-spec-experts" > Envoy?: Jeudi 15 Ao?t 2019 22:02:35 > Objet: Re: Refinements for sealed types >> So, A is implicitly sealed, but (IIRC) its lack of `permits` means >> that any class which is in the same compilation unit as A and which >> says `... extends A` is a permitted subtype. > > Currently, yes. > >> And, you are saying that it's not reasonable for A's author to have to >> oversee the whole compilation unit all the time, just in case some >> permitted subtype is lurking around with a `non-sealed` modifier that >> lets the X hierarchy be polluted yet further. > It's more about reading than writing.? Having both the sealed-ness be > implicit, and the permits list be implicit, puts a lot of strain on the > _reader_.? (Yes, Javadoc will spell it out, but source readers may not.) one way is to introduce more ceremony to make the implicit permit list more explicit, by forcing users to write "permits" for all sealed types and have a "permits subtypes" in case you want the compiler to infer the subtypes. R?mi From brian.goetz at oracle.com Sat Aug 17 14:11:45 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 17 Aug 2019 10:11:45 -0400 Subject: Draft specification for java.lang.Record In-Reply-To: <218857967.503581.1566039911517.JavaMail.zimbra@u-pem.fr> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> <218857967.503581.1566039911517.JavaMail.zimbra@u-pem.fr> Message-ID: <080b5264-1d35-4047-c291-23f1ad6acb9f@oracle.com> > Hi all, > why Record is a class (an abstract class) and not an interface ? It's a fair question.? At this point, it could go either way, and both have pros and cons. > Being an interface also means that we will be able to mix an inline > type and a record, an inline record?, for free, and more generally We are not ignoring this issue!? But I am not sure that "for free" is quite right.? It's a high priority to work out the abstract class / interface story for inline classes, and while j.l.Record is a tiny piece of that, there's way more to it.? So my preference is to figure out where we want to be on that story, and then adjust Record if need be. > Record being an interface is less disruptive than being a class when > you want to retrofit a class to be a record. I don't see this; if the class already has a superclass, you need to fix that before it becomes a record anyway. Another issue to be worked out is what are the degrees of freedom should we want to support abstract records in the future.? Also on the list. From brian.goetz at oracle.com Sat Aug 17 14:24:18 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 17 Aug 2019 10:24:18 -0400 Subject: Refinements for sealed types In-Reply-To: References: Message-ID: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> Since I got a few questions on this, let me step back a bit further and shed some light on these requirements. Sealed types are really *two* features in one: ?- Declaring a sum type, so that the sum constraint is visible to the compiler as a source of exhaustiveness in flow analysis (e.g., switch totality.) ?- A more refined notion of "final", that allows class authors to be able to reason about "I know where all the implementations of this type are". While the two fit (mostly) neatly into the same package, they serve very different audiences.? Many of the comments and questions we've gotten basically come down to assuming that one of these use cases is the "real" design goal, and the other is just a lucky accident. Users will, hopefully, declare sums (often sums of records) in all sorts of places.? These folks generally don't care about "I know all the implementations", because these classes often have no nontrivial implementation, they are data carriers. Platform developers are more likely to use sealing as a means of building safe APIs.? Think of how many classes are final -- for good reasons -- but we later wished there could be multiple implementations.? APIs like ConstantDesc are a primary example of the second sub-feature; we want to expose polymorphic APIs, but control the implementations.? (Historically we have resorted to using abstract classes with non-public constructors for that, but this is obviously a suboptimal move.) In that light, wanting to infer the permitted subtypes when they are co-declared is reducing ceremony for users of SubFeature 1, and and wanting to avoid accidental unsealing is an important safety feature for users of SubFeature 2. On 8/15/2019 1:38 PM, Brian Goetz wrote: > As Gavin has worked through the spec for sealed types, it has shone a > spotlight on one of the messy parts -- implicitly sealed types. I've > got an alternate way to stack this that I think is simpler and still > meets the goals. > > First, the goals (which in most cases align, but in some cases > conflict with each other): > > Unsealed concrete leaves should not be the default.? If we follow the > default strategy that a class declaration gets only the flags that are > declared explicitly (which is generally a good default), it is quite > likely that many subtypes of sealed types will be extensible, when > that was not the intent or understanding of the author.? For example, > in a hierarchy like the following: > > ??? sealed interface X permits A, B { } > ??? class A implements X { } > ??? class B implements X { } > > it is highly likely to be a source of mistakes that A and B are > neither sealed nor final, even though they are co-declared with the > sealed interface.? The author may well assume that they are in control > of all the implementations of X methods, when in fact anyone can > subclass A and override those methods. > > Avoiding excessive ceremony.? If the user had to declare every > concrete subtype as final, this may well be seen as excess ceremony.? > (This is the same reason we allow the permits clause to be inferred.) > > In the current design, we took the following path: > > ?- Subtypes of sealed types are implicitly sealed, unless marked > non-sealed. > ?- We infer a permits clause when it is omitted, which is possibly > empty (in which case the type is effectively final.) > > These are reasonable defaults, both from avoiding the safety question > of accidental extensibility, and reducing ceremony, but they interact > in some uncomfortable ways.? For example, under these rules, its > possible to have an explicit "permits" clause without saying "sealed", > which is a little strange.? And it is possible to infer both sealing > and the permits list, which might exceed our nontransparency comfort > level. > > So, let me propose a simplification: > > ?- A concrete subtype A of a sealed type X, which has no permits > clause, no known subtypes, and is not marked non-sealed, is implicitly > sealed (with an empty permits clause). > ?- Any other subtype of a sealed type must either have a "sealed" > modifier, or a "non-sealed" modifier. > ?- Any type with a permits list must have a sealed modifier. > > Rationale: This still manages to avoid the "accidental extension" > problem, and addresses both the extension and the ceremony problem > where they are both most severe -- at the leaves.? Abstract types > (which are generally fewer in number than leaves) err on the side of > explicitness. > > So the above hierarchy could be written as above, and A/B would be > implicitly final, as desired.? If A or B wanted to permit subtypes, > they would need to be explicitly marked non-sealed (to reopen them), > or explicitly sealed (with or without an explicit permits list.) > > I think this stays within the spirit of the goals, but with a little > less magic / complexity. > From forax at univ-mlv.fr Sat Aug 17 22:27:52 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 18 Aug 2019 00:27:52 +0200 (CEST) Subject: Refinements for sealed types In-Reply-To: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> Message-ID: <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Samedi 17 Ao?t 2019 16:24:18 > Objet: Re: Refinements for sealed types > Since I got a few questions on this, let me step back a bit further and > shed some light on these requirements. > > Sealed types are really *two* features in one: > > ?- Declaring a sum type, so that the sum constraint is visible to the > compiler as a source of exhaustiveness in flow analysis (e.g., switch > totality.) > ?- A more refined notion of "final", that allows class authors to be > able to reason about "I know where all the implementations of this type > are". > > While the two fit (mostly) neatly into the same package, they serve very > different audiences.? Many of the comments and questions we've gotten > basically come down to assuming that one of these use cases is the > "real" design goal, and the other is just a lucky accident. > > Users will, hopefully, declare sums (often sums of records) in all sorts > of places.? These folks generally don't care about "I know all the > implementations", because these classes often have no nontrivial > implementation, they are data carriers. > > Platform developers are more likely to use sealing as a means of > building safe APIs.? Think of how many classes are final -- for good > reasons -- but we later wished there could be multiple implementations. > APIs like ConstantDesc are a primary example of the second sub-feature; > we want to expose polymorphic APIs, but control the implementations. > (Historically we have resorted to using abstract classes with non-public > constructors for that, but this is obviously a suboptimal move.) > > In that light, wanting to infer the permitted subtypes when they are > co-declared is reducing ceremony for users of SubFeature 1, and and > wanting to avoid accidental unsealing is an important safety feature for > users of SubFeature 2. I agree about the premise of this mail, there are two ways to see a sealed type as a sum type or as a closed inheritance hierarchy. Reducing the ceremony is a good idea by itself but i think we are not acting logically here, There are several ways to reduce the ceremony - implicit declaration of sealed subtypes if the super type is sealed - implicit declaration of permit clauses and we may want to choose one, the other or both. So i think we should first defines the rules when everything is explicit and then add the rules that reduce the ceremony. So if everything is explicit, we want - all sealed types to defines their permit clauses (so we need a kind of "permit none" if no subtypes is allowed ?) - all subtypes of a seal types to explicitly says if there are sealed or non sealed (to avoid accidental unsealing). am i right, or am i missing something ? R?mi > > On 8/15/2019 1:38 PM, Brian Goetz wrote: >> As Gavin has worked through the spec for sealed types, it has shone a >> spotlight on one of the messy parts -- implicitly sealed types. I've >> got an alternate way to stack this that I think is simpler and still >> meets the goals. >> >> First, the goals (which in most cases align, but in some cases >> conflict with each other): >> >> Unsealed concrete leaves should not be the default.? If we follow the >> default strategy that a class declaration gets only the flags that are >> declared explicitly (which is generally a good default), it is quite >> likely that many subtypes of sealed types will be extensible, when >> that was not the intent or understanding of the author.? For example, >> in a hierarchy like the following: >> >> ??? sealed interface X permits A, B { } >> ??? class A implements X { } >> ??? class B implements X { } >> >> it is highly likely to be a source of mistakes that A and B are >> neither sealed nor final, even though they are co-declared with the >> sealed interface.? The author may well assume that they are in control >> of all the implementations of X methods, when in fact anyone can >> subclass A and override those methods. >> >> Avoiding excessive ceremony.? If the user had to declare every >> concrete subtype as final, this may well be seen as excess ceremony. >> (This is the same reason we allow the permits clause to be inferred.) >> >> In the current design, we took the following path: >> >> ?- Subtypes of sealed types are implicitly sealed, unless marked >> non-sealed. >> ?- We infer a permits clause when it is omitted, which is possibly >> empty (in which case the type is effectively final.) >> >> These are reasonable defaults, both from avoiding the safety question >> of accidental extensibility, and reducing ceremony, but they interact >> in some uncomfortable ways.? For example, under these rules, its >> possible to have an explicit "permits" clause without saying "sealed", >> which is a little strange.? And it is possible to infer both sealing >> and the permits list, which might exceed our nontransparency comfort >> level. >> >> So, let me propose a simplification: >> >> ?- A concrete subtype A of a sealed type X, which has no permits >> clause, no known subtypes, and is not marked non-sealed, is implicitly >> sealed (with an empty permits clause). >> ?- Any other subtype of a sealed type must either have a "sealed" >> modifier, or a "non-sealed" modifier. >> ?- Any type with a permits list must have a sealed modifier. >> >> Rationale: This still manages to avoid the "accidental extension" >> problem, and addresses both the extension and the ceremony problem >> where they are both most severe -- at the leaves.? Abstract types >> (which are generally fewer in number than leaves) err on the side of >> explicitness. >> >> So the above hierarchy could be written as above, and A/B would be >> implicitly final, as desired.? If A or B wanted to permit subtypes, >> they would need to be explicitly marked non-sealed (to reopen them), >> or explicitly sealed (with or without an explicit permits list.) >> >> I think this stays within the spirit of the goals, but with a little >> less magic / complexity. From brian.goetz at oracle.com Sun Aug 18 19:25:28 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 18 Aug 2019 15:25:28 -0400 Subject: Refinements for sealed types In-Reply-To: <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> Message-ID: <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> > So if everything is explicit, we want > - all sealed types to defines their permit clauses > (so we need a kind of "permit none" if no subtypes is allowed ?) > - all subtypes of a seal types to explicitly says if there are sealed or non sealed (to avoid accidental unsealing). > > am i right, or am i missing something ? Yes, this is basically it. I don?t think we need ?permits none? because a sealed type with no permitted subtypes is effectively a final type, and only makes sense for concrete classes, so that?s a final class. > There are several ways to reduce the ceremony > - implicit declaration of sealed subtypes if the super type is sealed > - implicit declaration of permit clauses > and we may want to choose one, the other or both. Right. Now, some observations: - We have two cases (sum types, and restricted hierarchies), and two goals (ceremony management, and safety/clarity); - Implicit sealing of subtypes by default serves both goals and both use cases, but asymmetrically; it provides ceremony reduction for the sum types case, and safety for the restricted hierarchy case. - Ceremony reduction is far more important for the ?sum types? use case than for the ?restricted hierarchy? use case, for several reasons. - Implicit declaration of permits clauses provides ceremony reduction for the sum types case. - Both forms of ceremony reduction start to cross over into being liabilities for the restricted-hierarchy case, because readers should have full information about the hierarchy shape. - Most sum types will be co-declared; many restricted hierarchies will not be. So, given all this, we should focus all our ceremony-reduction on the case of co-declared sum types. Which is mostly what I think I was suggesting: - Infer the permits clause when all the subtypes are co-declared; - Infer ?final? for leaf classes in a sum type; - Require explicitness in both sealed/non-sealed, and permits clause, in other cases. We could tighten this further, which probably makes sense: - Infer the permits clause when the subtypes are co-declared; - Infer final _when the current class is a subtype of a sealed type in the same compilation unit, and has no subtypes_ (again, co-declared with its parent); - Require explicit sealed or non-sealed in all other cases; - Require explicit permits clause in all other cases. So this focuses the magic on co-declared hierarchies. From brian.goetz at oracle.com Mon Aug 19 13:07:35 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 09:07:35 -0400 Subject: Refinements for sealed types In-Reply-To: <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> Message-ID: <70C9BB04-D2C3-4E8B-8270-42C606374EB4@oracle.com> >> There are several ways to reduce the ceremony >> - implicit declaration of sealed subtypes if the super type is sealed >> - implicit declaration of permit clauses >> and we may want to choose one, the other or both. For the record, there is also a third possible way: inferring `extends X`. I don?t particularly love it, especially as it is mutually exclusive with inferring the permits clause. But I include it for completeness. From alex.buckley at oracle.com Mon Aug 19 17:26:32 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 19 Aug 2019 10:26:32 -0700 Subject: Refinements for sealed types In-Reply-To: <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> Message-ID: <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> On 8/18/2019 12:25 PM, Brian Goetz wrote: > So, given all this, we should focus all our ceremony-reduction on the > case of co-declared sum types. ?Which is mostly what I think I was > suggesting: > > ?- Infer the permits clause when all the subtypes are co-declared; > ?- Infer ?final? for leaf classes in a sum type; > ?- Require explicitness in both sealed/non-sealed, and permits clause, > in other cases. How do you know from `sealed class X {}` and the rest of its compilation unit that all X's subtypes are co-declared? Maybe there's another class which extends X that someone forgot to pass to javac. Do you really mean to determine which subfeature is in use (and hence whether ceremony reduction is needed) based on what the host system can observe? I wondered if your intention is for a top-level sealed RECORD class to indicate sum-types code and a top-level sealed ABSTRACT class to indicate restricted-hierarchy code, but you downplayed abstract-superclass for restricted hierarchies earlier. Alex From brian.goetz at oracle.com Mon Aug 19 18:27:01 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 14:27:01 -0400 Subject: Refinements for sealed types In-Reply-To: <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> Message-ID: <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> >> So, given all this, we should focus all our ceremony-reduction on the >> case of co-declared sum types. ?Which is mostly what I think I was >> suggesting: >> >> ??- Infer the permits clause when all the subtypes are co-declared; >> ??- Infer ?final? for leaf classes in a sum type; >> ??- Require explicitness in both sealed/non-sealed, and permits >> clause, in other cases. > > How do you know from `sealed class X {}` and the rest of its > compilation unit that all X's subtypes are co-declared? By co-declared, I mean "in the same compilation unit." From alex.buckley at oracle.com Mon Aug 19 18:35:37 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 19 Aug 2019 11:35:37 -0700 Subject: Refinements for sealed types In-Reply-To: <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> Message-ID: <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> On 8/19/2019 11:27 AM, Brian Goetz wrote: >>> So, given all this, we should focus all our ceremony-reduction on the >>> case of co-declared sum types. ?Which is mostly what I think I was >>> suggesting: >>> >>> ??- Infer the permits clause when all the subtypes are co-declared; >>> ??- Infer ?final? for leaf classes in a sum type; >>> ??- Require explicitness in both sealed/non-sealed, and permits >>> clause, in other cases. >> >> How do you know from `sealed class X {}` and the rest of its >> compilation unit that all X's subtypes are co-declared? > > By co-declared, I mean "in the same compilation unit." The emphasis should be on the word "all", not "co-declared". How do you know that ALL of X's subtypes are declared in the same compilation unit? Alex From brian.goetz at oracle.com Mon Aug 19 18:52:16 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 14:52:16 -0400 Subject: Refinements for sealed types In-Reply-To: <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> Message-ID: <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> >>> How do you know from `sealed class X {}` and the rest of its >>> compilation unit that all X's subtypes are co-declared? >> >> By co-declared, I mean "in the same compilation unit." > > The emphasis should be on the word "all", not "co-declared". How do > you know that ALL of X's subtypes are declared in the same compilation > unit? Here's what I'm suggesting. If you say ??? sealed interface X { ... } with no permits clause, then the permits clause is inferred from the contents of the compilation unit, which is _by definition_ all the permitted subtypes.? (If there are no subtypes in the current compilation unit, a warning may be in order.) Similarly, if you have a subtype of X: ??? sealed interface X { ??????? class A implements X { } ? ? } that is _in the same compilation unit_, then we will infer `sealed` on A unless you say otherwise. In all other cases, subtypes of sealed types must either say "sealed" or "non-sealed" (or "final"). From alex.buckley at oracle.com Mon Aug 19 20:50:06 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 19 Aug 2019 13:50:06 -0700 Subject: Refinements for sealed types In-Reply-To: <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> Message-ID: <685da175-abd3-d9d1-7426-264785932495@oracle.com> On 8/19/2019 11:52 AM, Brian Goetz wrote: >>>> How do you know from `sealed class X {}` and the rest of its >>>> compilation unit that all X's subtypes are co-declared? >>> >>> By co-declared, I mean "in the same compilation unit." >> >> The emphasis should be on the word "all", not "co-declared". How do >> you know that ALL of X's subtypes are declared in the same compilation >> unit? > > Here's what I'm suggesting. > > If you say > > ??? sealed interface X { ... } > > with no permits clause, then the permits clause is inferred from the > contents of the compilation unit, which is _by definition_ all the > permitted subtypes.? (If there are no subtypes in the current > compilation unit, a warning may be in order.) OK. > Similarly, if you have a subtype of X: > > ??? sealed interface X { > ??????? class A implements X { } > ? ? } > > that is _in the same compilation unit_, then we will infer `sealed` on A > unless you say otherwise. To be clear, infer `final` on A. We were already inferring `sealed` last week; the new thing this week is to infer A's `permits` list as empty (giving A an overall score of implicitly `final`) rather than "any co-declared subtypes of A". Given the code above, it is a compile-time error for any class in any compilation unit to attempt to subclass A. Where this is all headed is that we never infer `sealed` with a non-empty `permits`. As you said, no need for `permits none`. It is good to draw a clear distinction between `sealed`-with-at-least-one-permitted-subtype versus `final`-with-zero-subtypes. Alex From brian.goetz at oracle.com Mon Aug 19 22:22:35 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 18:22:35 -0400 Subject: Refinements for sealed types In-Reply-To: <685da175-abd3-d9d1-7426-264785932495@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> Message-ID: <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> > To be clear, infer `final` on A. We were already inferring `sealed` > last week; the new thing this week is to infer A's `permits` list as > empty (giving A an overall score of implicitly `final`) rather than > "any co-declared subtypes of A". Given the code above, it is a > compile-time error for any class in any compilation unit to attempt to > subclass A. I don't have a strong opinion on whether "sealed but no permitted subtypes" is a habitable space separate from "final", but I'm fine to say "that means final" for most purposes.? Not sure what reflection should say; is it OK for a type that is clearly sealed in its declaration to report back as "not sealed, but final?"? Or does that mean it is both sealed _and_ final (an empty PermittedSubtypes attribute, plus an ACC_FINAL)?? Similarly, depending on where we land on this decision, it affects the binary compatibility consequences (e.g., is it binary compatible to go from final to sealed (with or without any permitted subtypes)? > Where this is all headed is that we never infer `sealed` with a > non-empty `permits`. As you said, no need for `permits none`. It is > good to draw a clear distinction between > `sealed`-with-at-least-one-permitted-subtype versus > `final`-with-zero-subtypes. I'm fine with this, modulo above details-to-be-worked-out. From forax at univ-mlv.fr Mon Aug 19 22:35:25 2019 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 20 Aug 2019 00:35:25 +0200 (CEST) Subject: Refinements for sealed types In-Reply-To: <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> Message-ID: <721457287.192835.1566254125338.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Dimanche 18 Ao?t 2019 21:25:28 > Objet: Re: Refinements for sealed types >> So if everything is explicit, we want >> - all sealed types to defines their permit clauses >> (so we need a kind of "permit none" if no subtypes is allowed ?) >> - all subtypes of a seal types to explicitly says if there are sealed or non >> sealed (to avoid accidental unsealing). >> am i right, or am i missing something ? > Yes, this is basically it. I don?t think we need ?permits none? because a sealed > type with no permitted subtypes is effectively a final type, and only makes > sense for concrete classes, so that?s a final class. And the followup question is "is it ok to have an interface declared final ?" >> There are several ways to reduce the ceremony >> - implicit declaration of sealed subtypes if the super type is sealed >> - implicit declaration of permit clauses >> and we may want to choose one, the other or both. > Right. Now, some observations: > - We have two cases (sum types, and restricted hierarchies), and two goals > (ceremony management, and safety/clarity); > - Implicit sealing of subtypes by default serves both goals and both use cases, > but asymmetrically; it provides ceremony reduction for the sum types case, and > safety for the restricted hierarchy case. > - Ceremony reduction is far more important for the ?sum types? use case than for > the ?restricted hierarchy? use case, for several reasons. > - Implicit declaration of permits clauses provides ceremony reduction for the > sum types case. > - Both forms of ceremony reduction start to cross over into being liabilities > for the restricted-hierarchy case, because readers should have full information > about the hierarchy shape. > - Most sum types will be co-declared; many restricted hierarchies will not be. > So, given all this, we should focus all our ceremony-reduction on the case of > co-declared sum types. Which is mostly what I think I was suggesting: > - Infer the permits clause when all the subtypes are co-declared; > - Infer ?final? for leaf classes in a sum type; > - Require explicitness in both sealed/non-sealed, and permits clause, in other > cases. ok, i prefer this explanation, it's clearer at least for me, > We could tighten this further, which probably makes sense: > - Infer the permits clause when the subtypes are co-declared; > - Infer final _when the current class is a subtype of a sealed type in the same > compilation unit, and has no subtypes_ (again, co-declared with its parent); > - Require explicit sealed or non-sealed in all other cases; > - Require explicit permits clause in all other cases. > So this focuses the magic on co-declared hierarchies. I prefer this version. R?mi From forax at univ-mlv.fr Mon Aug 19 22:40:40 2019 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 20 Aug 2019 00:40:40 +0200 (CEST) Subject: Refinements for sealed types In-Reply-To: <70C9BB04-D2C3-4E8B-8270-42C606374EB4@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <70C9BB04-D2C3-4E8B-8270-42C606374EB4@oracle.com> Message-ID: <1885903972.192953.1566254440842.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Remi Forax" > Cc: "amber-spec-experts" > Envoy?: Lundi 19 Ao?t 2019 15:07:35 > Objet: Re: Refinements for sealed types >>> There are several ways to reduce the ceremony >>> - implicit declaration of sealed subtypes if the super type is sealed >>> - implicit declaration of permit clauses >>> and we may want to choose one, the other or both. > For the record, there is also a third possible way: inferring `extends X`. I > don?t particularly love it, especially as it is mutually exclusive with > inferring the permits clause. But I include it for completeness. We tried that by saying if you are inside the declaration of a sealed type, the extends can be inferred but it doesn't play well with inner classes that are already using the fact that there are enclosed to have another semantics. Basically going in that direction means separating the sum type use case (which in that case will have its own syntax) and the closed hierarchy use case (which can use the keyword sealed). R?mi From forax at univ-mlv.fr Mon Aug 19 22:42:10 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 20 Aug 2019 00:42:10 +0200 (CEST) Subject: Refinements for sealed types In-Reply-To: <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> References: <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> Message-ID: <203513286.192996.1566254530750.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Alex Buckley" , "amber-spec-experts" > Envoy?: Mardi 20 Ao?t 2019 00:22:35 > Objet: Re: Refinements for sealed types >> To be clear, infer `final` on A. We were already inferring `sealed` >> last week; the new thing this week is to infer A's `permits` list as >> empty (giving A an overall score of implicitly `final`) rather than >> "any co-declared subtypes of A". Given the code above, it is a >> compile-time error for any class in any compilation unit to attempt to >> subclass A. > > I don't have a strong opinion on whether "sealed but no permitted > subtypes" is a habitable space separate from "final", but I'm fine to > say "that means final" for most purposes.? Not sure what reflection > should say; is it OK for a type that is clearly sealed in its > declaration to report back as "not sealed, but final?"? Or does that > mean it is both sealed _and_ final (an empty PermittedSubtypes > attribute, plus an ACC_FINAL)?? Similarly, depending on where we land on > this decision, it affects the binary compatibility consequences (e.g., > is it binary compatible to go from final to sealed (with or without any > permitted subtypes)? > >> Where this is all headed is that we never infer `sealed` with a >> non-empty `permits`. As you said, no need for `permits none`. It is >> good to draw a clear distinction between >> `sealed`-with-at-least-one-permitted-subtype versus >> `final`-with-zero-subtypes. > > I'm fine with this, modulo above details-to-be-worked-out. I would prefer "sealed" to be ACC_FINAL in the bytecode and PermittedSubtypes to list at least one subtype, basically from the VM POV having an attribute PermittedSubtypes weaken the fact that the type is final by allowing subtypes. It may cause some already existing code to run afoul by i think we should try this runtime encoding and see if it breaks a lot of thing or not. R?mi From alex.buckley at oracle.com Mon Aug 19 22:52:50 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 19 Aug 2019 15:52:50 -0700 Subject: Refinements for sealed types In-Reply-To: <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> Message-ID: <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> On 8/19/2019 3:22 PM, Brian Goetz wrote: > I don't have a strong opinion on whether "sealed but no permitted > subtypes" is a habitable space separate from "final", but I'm fine to > say "that means final" for most purposes. Not sure what reflection > should say; is it OK for a type that is clearly sealed in its > declaration to report back as "not sealed, but final?" Or does > that mean it is both sealed _and_ final (an empty PermittedSubtypes > attribute, plus an ACC_FINAL)? Are you referring here to a source declaration such as `sealed interface X {}` where, per Vicente, X.class has ACC_FINAL set? I think X.class.isSealed() should return true and X.class.isFinal() [simplifying for space] should return false. Has there been any discussion of reclaiming ACC_SUPER, 0x0020, for ACC_SEALED in v58 class files? Using an empty attribute to denote sealing is pretty ugly. Alex From brian.goetz at oracle.com Mon Aug 19 22:58:23 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 18:58:23 -0400 Subject: Refinements for sealed types In-Reply-To: <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> Message-ID: I was suggesting that a class: ??? sealed class X { } with no permitted subtypes would be _both_ ACC_FINAL *and* have an empty sealing attribute.? The rationale would be: ?- Reflects intuition that sealed-with-no-permitted-subtypes really means final, but ?- Allows reflection to distinguish between the two. Maybe the reflection behavior isn't important, at which point we just drop the latter. On 8/19/2019 6:52 PM, Alex Buckley wrote: > On 8/19/2019 3:22 PM, Brian Goetz wrote: >> I don't have a strong opinion on whether "sealed but no permitted >> subtypes" is a habitable space separate from "final", but I'm fine to >> say "that means final" for most purposes.? Not sure what reflection >> should say; is it OK for a type that is clearly sealed in its >> declaration to report back as "not sealed, but final?"? Or does >> that mean it is both sealed _and_ final (an empty PermittedSubtypes >> attribute, plus an ACC_FINAL)? > > Are you referring here to a source declaration such as `sealed interface > X {}` where, per Vicente, X.class has ACC_FINAL set? I think > X.class.isSealed() should return true and X.class.isFinal() > [simplifying for space] should return false. Has there been any > discussion of reclaiming ACC_SUPER, 0x0020, for ACC_SEALED in v58 > class files? Using an empty attribute to denote sealing is pretty ugly. > > Alex From joe.darcy at oracle.com Tue Aug 20 00:18:34 2019 From: joe.darcy at oracle.com (Joe Darcy) Date: Mon, 19 Aug 2019 17:18:34 -0700 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> Message-ID: Hello, Are there plans for methods to take a string and produce value-preserving escape sequence encodings of it, quoting trailing whitespace, etc.? Cheers, -Joe On 8/13/2019 5:46 AM, Jim Laskey wrote: > https://bugs.openjdk.java.net/browse/JDK-8227870 > > Comment back to this list, thank you. > > Cheers, > > -- Jim > > > From brian.goetz at oracle.com Tue Aug 20 00:54:45 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Aug 2019 20:54:45 -0400 Subject: Escape Sequences For Managing Whitespace (Preview) In-Reply-To: References: <7A4842DE-4D60-496D-823E-7E38DAB0A6FC@oracle.com> Message-ID: <3035F91D-D21E-4DE6-9542-529C8FAF546D@oracle.com> In an earlier round there was a pair of methods, which were variously called escape/unescape, encode/decodeEscapes, etc. No one could remember which was in which direction ? there is currently not such a method. > On Aug 19, 2019, at 8:18 PM, Joe Darcy wrote: > > Hello, > > Are there plans for methods to take a string and produce value-preserving escape sequence encodings of it, quoting trailing whitespace, etc.? > > Cheers, > > -Joe > > On 8/13/2019 5:46 AM, Jim Laskey wrote: >> https://bugs.openjdk.java.net/browse/JDK-8227870 >> >> Comment back to this list, thank you. >> >> Cheers, >> >> -- Jim >> >> >> From brian.goetz at oracle.com Tue Aug 20 19:40:56 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 20 Aug 2019 15:40:56 -0400 Subject: Refinements for sealed types In-Reply-To: References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> Message-ID: <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> Gathering the various threads in this discussion, here?s what seems a sensible landing place: 1. A sealed _class_ with no permitted subtypes is a final class. The distinction between declared-sealed-with-no-permits and declared-final is erased away. [*] 2. Sealed abstract classes and interfaces with no permitted subtypes are illegal. 3. If a sealed type does not specify a permits list, it can be inferred by enumerating the subtypes in the same compilation unit. 4. If a concrete class is a subtype of a sealed type declared in the same compilation unit, and none of the modifiers { sealed, non-sealed, final } are present, it is inferred to be sealed with an inferred permits list (which, according to (1), may be treated as final if there are none.) 5. (optional) It is an error to specify an explicit permits list if `sealed` is not specified. 6. Otherwise, a subtype of a sealed type must have exactly one of the following modifiers: sealed, non-sealed, final. Benefits: - No need to support ACC_FINAL in conjunction with ABSTRACT or INTERFACE. - Inference of both sealed and permits clauses are purely local ? there is no action-at-a-distance (for distances greater than one compilation unit.) - Sealed-ness for subtypes of sealed classes declared separately must be explicit. - Co-declared hierarchies get significant ceremony reduction. [*] This forecloses on directly having ?dynamic subtypes? of sealed types, in the same way we can have dynamic nest mates via Lookup.defineClass(). However, there are reasonable workarounds for this should it become desirable. > On Aug 19, 2019, at 6:58 PM, Brian Goetz wrote: > > I was suggesting that a class: > > sealed class X { } > > with no permitted subtypes would be _both_ ACC_FINAL *and* have an empty sealing attribute. The rationale would be: > > - Reflects intuition that sealed-with-no-permitted-subtypes really means final, but > - Allows reflection to distinguish between the two. > > Maybe the reflection behavior isn't important, at which point we just drop the latter. > > On 8/19/2019 6:52 PM, Alex Buckley wrote: >> On 8/19/2019 3:22 PM, Brian Goetz wrote: >>> I don't have a strong opinion on whether "sealed but no permitted subtypes" is a habitable space separate from "final", but I'm fine to >>> say "that means final" for most purposes. Not sure what reflection >>> should say; is it OK for a type that is clearly sealed in its declaration to report back as "not sealed, but final?" Or does >>> that mean it is both sealed _and_ final (an empty PermittedSubtypes attribute, plus an ACC_FINAL)? >> >> Are you referring here to a source declaration such as `sealed interface >> X {}` where, per Vicente, X.class has ACC_FINAL set? I think X.class.isSealed() should return true and X.class.isFinal() [simplifying for space] should return false. Has there been any discussion of reclaiming ACC_SUPER, 0x0020, for ACC_SEALED in v58 class files? Using an empty attribute to denote sealing is pretty ugly. >> >> Alex > From alex.buckley at oracle.com Tue Aug 20 21:56:26 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 20 Aug 2019 14:56:26 -0700 Subject: Refinements for sealed types In-Reply-To: <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> Message-ID: On 8/20/2019 12:40 PM, Brian Goetz wrote: > Gathering the various threads in this discussion, here?s what seems a sensible landing place: > > 1. A sealed _class_ with no permitted subtypes is a final class. The distinction between declared-sealed-with-no-permits and declared-final is erased away. [*] > > 2. Sealed abstract classes and interfaces with no permitted subtypes are illegal. > > 3. If a sealed type does not specify a permits list, it can be inferred by enumerating the subtypes in the same compilation unit. > > 4. If a concrete class is a subtype of a sealed type declared in the same compilation unit, and none of the modifiers { sealed, non-sealed, final } are present, it is inferred to be sealed with an inferred permits list (which, according to (1), may be treated as final if there are none.) > > 5. (optional) It is an error to specify an explicit permits list if `sealed` is not specified. > > 6. Otherwise, a subtype of a sealed type must have exactly one of the following modifiers: sealed, non-sealed, final. I see two committed principles here: 1. Permitted subtypes are common; their enumeration isn't. It's almost like explicit `permits` is a separable feature that could be set aside for now. 2. Ceremony reduction is important enough to treat concrete direct subclasses of a sealed type specially. However, that special treatment comes at the expense of another principle: "leaf nodes [implicitly final classes] are obvious". I was hoping to have this because implicitly final classes [sealed with zero permitted subtypes] are The Show. Below, it's kinda hard to figure out that class C is NOT a leaf node: --I.java-- sealed interface I {} // implicitly `permits C` class C implements I {} // implicitly `sealed` and `permits D` ... lots of other stuff ... class D extends C {} // implicitly `sealed` w/ no permitted subtypes ---------- whereas below, the leaf nodes are mostly obvious by virtue of how the intermediate hierarchy is explicitly `sealed`: --I.java-- sealed interface I {} // implicitly `permits J` sealed interface J extends I {} // plainly not a leaf sealed abstract class AC implements J {} // plainly not a leaf class PolarData extends AC {} // maybe a leaf class CartesianData extends AC {} // maybe a leaf // EOF, so yes, PolarData and CartesianData are indeed leaves ---------- tl;dr We're back to having both sealed-ness and the potentially-non-trivial permits list be implicit, which was earlier thought to strain the reader. If we said that concrete direct subclasses of a sealed type were implicitly final, then ceremony reduction would still be high, and if you really don't want them to be leaf nodes then say `sealed` or `non-sealed`. Alex From brian.goetz at oracle.com Tue Aug 20 22:45:49 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 20 Aug 2019 18:45:49 -0400 Subject: Refinements for sealed types In-Reply-To: References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> Message-ID: > I see two committed principles here: > > 1. Permitted subtypes are common; their enumeration isn't. It's almost like explicit `permits` is a separable feature that could be set aside for now. > > 2. Ceremony reduction is important enough to treat concrete direct subclasses of a sealed type specially. I am not sure I would raise these to the level of ?principle?, as much as ?expectation?. In the typical co-declared sum type case, there may be many cases: sealed interface Letter { class A implements Letter { } class B implements Letter { } ? class Z implements Letter { } } Having to say ?permits A, B, .. Z? here is both verbose (lots of cases) and seems pretty repetitive (the subtypes are RIGHT THERE.) Same, to a lesser degree, with having to say ?sealed? or ?final? on each case. In the typical ?restricted hierarchy? case, I expect there to be fewer subtypes (less fan-out), less co-declaration (will likely want to use top level classes), and simply fewer instances of this use (mostly, this will be used by platforms and libraries, rather than applications.) So the ceremony here is (a) less substantial, (b) less frequent, and (c) more beneficial. So basically, this is special treatment for co-declared sums. (We?re still more verbose than Haskell, even with these, sadly.) > However, that special treatment comes at the expense of another principle: "leaf nodes [implicitly final classes] are obvious". I was hoping to have this because implicitly final classes [sealed with zero permitted subtypes] are The Show. Below, it's kinda hard to figure out that class C is NOT a leaf node: > > --I.java-- > sealed interface I {} // implicitly `permits C` > > class C implements I {} // implicitly `sealed` and `permits D` > > ... lots of other stuff ... > class D extends C {} // implicitly `sealed` w/ no permitted subtypes > ---------- > > whereas below, the leaf nodes are mostly obvious by virtue of how the intermediate hierarchy is explicitly `sealed`: > > --I.java-- > sealed interface I {} // implicitly `permits J` > sealed interface J extends I {} // plainly not a leaf > sealed abstract class AC implements J {} // plainly not a leaf > > class PolarData extends AC {} // maybe a leaf > class CartesianData extends AC {} // maybe a leaf > // EOF, so yes, PolarData and CartesianData are indeed leaves > ---------- > > tl;dr We're back to having both sealed-ness and the potentially-non-trivial permits list be implicit, which was earlier thought to strain the reader. If we said that concrete direct subclasses of a sealed type were implicitly final, then ceremony reduction would still be high, and if you really don't want them to be leaf nodes then say `sealed` or `non-sealed`. This seems reasonable too, because most concrete classes in such hierarchies _will be_ leaves. In this world, though, it feels a little inconsistent to only do this when the subclass and the sealed type are in the same compilation unit; we could extend this to all concrete subclasses of sealed types (implicitly final, unless they say sealed or non-sealed). This is consistent, but feels a little more action-at-a-distance-y. So, its pick your poison. From alex.buckley at oracle.com Tue Aug 20 23:37:00 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 20 Aug 2019 16:37:00 -0700 Subject: Refinements for sealed types In-Reply-To: References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> Message-ID: <4152e6c2-df2c-6f78-fa70-e0d5535d6ae5@oracle.com> On 8/20/2019 3:45 PM, Brian Goetz wrote: > This seems reasonable too, because most concrete classes in such > hierarchies _will be_ leaves. In this world, though, it feels a > little inconsistent to only do this when the subclass and the sealed > type are in the same compilation unit; we could extend this to all > concrete subclasses of sealed types (implicitly final, unless they > say sealed or non-sealed). This is consistent, but feels a little > more action-at-a-distance-y. So, its pick your poison. You often show the concrete classes as members of a sealed interface. Interface members are already implicitly public and static; is this a precedent to build on for a sealed interface? That is, have the nested concrete classes be implicitly final, and have the interface's implicit `permits` list care about only nested concrete classes. Top level concrete classes in the same compilation unit would be handed like concrete classes in other compilation units: nothing different than today. Layering sealing on top of nesting has the attraction that it avoids putting multiple public concrete classes in a single compilation unit. It's right that the concrete leaves are public, but javac dislikes compilation units with multiple public types. Alex From stuart.marks at oracle.com Tue Aug 20 23:46:48 2019 From: stuart.marks at oracle.com (Stuart Marks) Date: Tue, 20 Aug 2019 16:46:48 -0700 Subject: Draft specification for java.lang.Record In-Reply-To: <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> References: <445ca409-8807-173f-57c9-d5cb7b76da5a@oracle.com> <5f08fe7b-4cff-e828-295c-bc9bf79c2809@oracle.com> Message-ID: It might be worth also saying something about finalization. I think the choices are to do nothing (be finalizer-agnostic) or to prohibit it. Generating a finalize() method for every record class seems like a non-starter. I'm leaning toward prohibiting finalization, for a couple reasons. First, Object.finalize() is deprecated, and finalization will eventually be removed, so we'd like to discourage new code from using finalizers. Second, if a user-provided record constructor throws an exception, the partially-initialized record instance will be visible to the finalizer and it potentially can be resurrected. This seems like a really bad idea. Suggestion: /** * Finalization of record class instances is disallowed. * * @implSpec * This method implementation does nothing. */ protected final void finalize() { } s'marks On 8/15/19 10:34 AM, Brian Goetz wrote: > > > Re-sending as plain text, since the formatting got mangled by mailers. > > > /* > ?* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. > ?* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. > ?* > ?* This code is free software; you can redistribute it and/or modify it > ?* under the terms of the GNU General Public License version 2 only, as > ?* published by the Free Software Foundation.? Oracle designates this > ?* particular file as subject to the "Classpath" exception as provided > ?* by Oracle in the LICENSE file that accompanied this code. > ?* > ?* This code is distributed in the hope that it will be useful, but WITHOUT > ?* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > ?* FITNESS FOR A PARTICULAR PURPOSE.? See the GNU General Public License > ?* version 2 for more details (a copy is included in the LICENSE file that > ?* accompanied this code). > ?* > ?* You should have received a copy of the GNU General Public License version > ?* 2 along with this work; if not, write to the Free Software Foundation, > ?* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. > ?* > ?* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA > ?* or visit www.oracle.com if you need additional information or have any > ?* questions. > ?*/ > package java.lang; > > /** > ?* This is the common base class of all Java language record classes. > ?* > ?*

More information about records, including descriptions of the > ?* implicitly declared methods synthesized by the compiler, can be > ?* found in section 8.10 of > ?* The Java™ Language Specification. > ?* > ?*

A record class is a shallowly immutable, transparent carrier for > ?* a fixed set of values, called the record components.? The Java™ > ?* language provides concise syntax for declaring record classes, whereby the > ?* record components are declared in the record header.? The list of record > ?* components declared in the record header form the record descriptor. > ?* > ?*

A record class has the following mandated members: a public canonical > ?* constructor, whose descriptor is the same as the record descriptor; > ?* a private static field corresponding to each component, whose name and > ?* type are the same as that of the component; a public accessor method > ?* corresponding to each component, whose name and return type are the same as > ?* that of the component.? If not explicitly declared in the body of the record, > ?* implicit implementations for these members are provided. > ?* > ?*

The implicit declaration of the canonical constructor initializes the > ?* component fields from the corresponding constructor arguments. The implicit > ?* declaration of the accessor methods returns the value of the corresponding > ?* component field.? The implicit declaration of the {@link > Object#equals(Object)}, > ?* {@link Object#hashCode()}, and {@link Object#toString()} methods are derived > ?* from all of the component fields. > ?* > ?*

The primary reasons to provide an explicit declaration for the > ?* canonical constructor or accessor methods are to validate constructor > ?* arguments, perform defensive copies on mutable components, or normalize groups > ?* of components (such as reducing a rational number to lowest terms.) If any > ?* of these are provided explicitly. > ?* > ?*

For all record classes, the following invariant must hold: if a record R's > ?* components are {@code c1, c2, ... cn}, then if a record instance is copied > ?* as follows: > ?*

>  ?*???? R copy = new R(r.c1(), r.c2(), ..., r.cn());
>  ?* 
> ?* then it must be the case that {@code r.equals(copy)}. > ?* > ?* @jls 8.10 > ?* @since 14 > ?*/ > public abstract class Record { > ??? /** > ???? * Indicates whether some other object is "equal to" this one.? In addition > ???? * to the general contract of {@link Object#equals(Object)}, > ???? * record classes must further participate in the invariant that when > ???? * a record instance is "copied" by passing the result of the record component > ???? * accessor methods to the canonical constructor, the resulting copy is > ???? * equal to the original instance. > ???? * > ???? * @implNote > ???? * The implicitly provided implementation returns {@code true} if and > ???? * only if the argument is an instance of the same record type as this object, > ???? * and each component of this record is equal to the corresponding component > ???? * of the argument, according to {@link Object#equals(Object)} for components > ???? * whose types are reference types, and {@code ==} for components whose > ???? * types are primitive types. > ???? * > ???? * @see Object#equals(Object) > ???? * > ???? * @param?? obj?? the reference object with which to compare. > ???? * @return? {@code true} if this object is the same as the obj > ???? *????????? argument; {@code false} otherwise. > ???? */ > ??? @Override > ??? public abstract boolean equals(Object obj); > > ??? /** > ???? * {@inheritDoc} > ???? * > ???? * @implNote > ???? * The implicitly provided implementation returns a hash code value derived > ???? * by combining the hash code value for all the components, according to > ???? * {@link Object#hashCode()} for components whose types are reference types, > ???? * or the primitive wrapper hash code for components whose types are primitive > ???? * types. > ???? * > ???? * @see???? Object#hashCode() > ???? * > ???? * @return? a hash code value for this object. > ???? */ > ??? @Override > ??? public abstract int hashCode(); > > ??? /** > ???? * {@inheritDoc} > ???? * > ???? * @implNote > ???? * The implicitly provided implementation returns a string that is derived > ???? * from the name of the record class and the names and string representations > ???? * of all the components, according to {@link Object#toString()} for > components > ???? * whose types are reference types, and the primitive wrapper {@code toString} > ???? * method for components whose types are primitive types. > ???? * > ???? * @see???? Object#toString() () > ???? * > ???? * @return? a string representation of the object. > ???? */ > ??? @Override > ??? public abstract String toString(); > } From brian.goetz at oracle.com Wed Aug 21 00:05:15 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 20 Aug 2019 20:05:15 -0400 Subject: Refinements for sealed types In-Reply-To: <4152e6c2-df2c-6f78-fa70-e0d5535d6ae5@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> <4152e6c2-df2c-6f78-fa70-e0d5535d6ae5@oracle.com> Message-ID: <609E48A8-9540-4A9E-9CA5-83B039FD0C62@oracle.com> I do, because the other way to get a class into the same compilation unit, aux classes, have some limitations. So we?re encouraging the pattern of nesting. But ? I am not sure we want to push it all the way. Consider a type like: sealed interface Fruit { interface RedFruit extends Node { } interface BlueFruit extends Node { } class Strawberry implements RedFruit { } class Raspberry implements RedFruit { } class Blueberry implements BlueFruit { } } Do we want to force Blueberry to be Fruit.BlueFruit.Blueberry (or at least, twist the user?s arm into it by offering less ceremony?) I think that would be lame ? and worse than lame if the intermediate interfaces (RedFruit, BlueFruit) were not public. Then we?d be nesting the public types in the nonpublic ones, and they?d be inaccessible. > You often show the concrete classes as members of a sealed interface. Interface members are already implicitly public and static; is this a precedent to build on for a sealed interface? That is, have the nested concrete classes be implicitly final, and have the interface's implicit `permits` list care about only nested concrete classes. Top level concrete classes in the same compilation unit would be handed like concrete classes in other compilation units: nothing different than today. > > Layering sealing on top of nesting has the attraction that it avoids putting multiple public concrete classes in a single compilation unit. It's right that the concrete leaves are public, but javac dislikes compilation units with multiple public types. > > Alex From alex.buckley at oracle.com Wed Aug 21 19:38:58 2019 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 21 Aug 2019 12:38:58 -0700 Subject: Refinements for sealed types In-Reply-To: <609E48A8-9540-4A9E-9CA5-83B039FD0C62@oracle.com> References: <3b4ad905-0872-5a9c-c600-0b9bc582d69c@oracle.com> <1076154877.523833.1566080872290.JavaMail.zimbra@u-pem.fr> <50C6E299-95EA-42B1-B395-CDF57FFCD5DA@oracle.com> <32016152-f5ee-7d15-01d3-c40055c3a8f8@oracle.com> <58ac9535-edac-a710-5826-786fa02cbf64@oracle.com> <1bdd2ea8-a4f3-299b-6e78-02e3ee9bb0c0@oracle.com> <3e6e7ac7-8629-5110-e7d2-a8eb33a37152@oracle.com> <685da175-abd3-d9d1-7426-264785932495@oracle.com> <54c7ea28-9073-2c3a-dc42-3f4ada952dc2@oracle.com> <6c7f45a4-c061-f458-c8a8-e53b61673c20@oracle.com> <81907C17-9837-449B-91BB-296A6872A06E@oracle.com> <4152e6c2-df2c-6f78-fa70-e0d5535d6ae5@oracle.com> <609E48A8-9540-4A9E-9CA5-83B039FD0C62@oracle.com> Message-ID: <7edd5296-2bfb-6ac7-4535-e0f8d1e04db6@oracle.com> // Declarations of RedFruit/BlueFruit corrected below from `extends Node` to `extends Fruit` I don't know what I don't know about "aux classes". I do know that if you don't nest the concrete leaves, they can't be public in the same compilation unit: --Fruit.java-- public sealed interface Fruit { // permits RedFruit, BlueFruit /* public */ sealed interface RedFruit extends Fruit {} // permits *berry /* public */ sealed interface BlueFruit extends Fruit {} // permits Blueberry /* public sealed */ class FruitCake implements Fruit {} // permits nothing; implicitly final } /* package-access */ class Strawberry implements Fruit.RedFruit {} /* package-access */ class Raspberry implements Fruit.RedFruit {} /* package-access */ class Blueberry implements Fruit.BlueFruit {} -------------- Putting the concrete leaves in another compilation unit so they can be public (assume that's the right accessibility) isn't ceremony reduction. Am I missing something about how this hierarchy should be declared? Alex On 8/20/2019 5:05 PM, Brian Goetz wrote: > I do, because the other way to get a class into the same compilation unit, aux classes, have some limitations. So we?re encouraging the pattern of nesting. But ? I am not sure we want to push it all the way. Consider a type like: > > sealed interface Fruit { > interface RedFruit extends Fruit { } > interface BlueFruit extends Fruit { } > class Strawberry implements RedFruit { } > class Raspberry implements RedFruit { } > class Blueberry implements BlueFruit { } > } > > Do we want to force Blueberry to be Fruit.BlueFruit.Blueberry (or at least, twist the user?s arm into it by offering less ceremony?) I think that would be lame ? and worse than lame if the intermediate interfaces (RedFruit, BlueFruit) were not public. Then we?d be nesting the public types in the nonpublic ones, and they?d be inaccessible. > >> You often show the concrete classes as members of a sealed interface. Interface members are already implicitly public and static; is this a precedent to build on for a sealed interface? That is, have the nested concrete classes be implicitly final, and have the interface's implicit `permits` list care about only nested concrete classes. Top level concrete classes in the same compilation unit would be handed like concrete classes in other compilation units: nothing different than today. >> >> Layering sealing on top of nesting has the attraction that it avoids putting multiple public concrete classes in a single compilation unit. It's right that the concrete leaves are public, but javac dislikes compilation units with multiple public types. >> >> Alex > From info at j-kuhn.de Fri Aug 23 12:39:38 2019 From: info at j-kuhn.de (Johannes Kuhn) Date: Fri, 23 Aug 2019 14:39:38 +0200 Subject: JEP 348: The wrong way? Message-ID: <7ecdf38a-5d52-f8e5-ab30-8039a4a19791@j-kuhn.de> "JEP 348: Compiler Intrinsics for Java SE APIs"'s most prominent use case is String::format. In this use case, we suspect to gain some performance improvement if the format string doesn't have to be parsed on each invocation. I wonder if this is not something the JIT could do, with some help from the developers. In short, the JIT might reuse the result of a @Pure method instead of calling it again. Declaring methods as @Pure and let the JIT figure out the details has probably other uses, but for now keep the discussion on String::format. If the parsed format can be cached by the JIT, existing users of String::format could benefit, while also avoiding two different implementations that have to be keep in sync. I don't claim that this solution is simpler (it probably is not) or better (run the numbers), just a suggestion. JEP 348 doesn't come for free. Once committed to it, the bootstrap methods have to stay forever, even if they only return a ConstantCallSite later. I hope that you will evaluate my suggestion and don't commit to early to an implementation that turns out to be the wrong way later. With best regards, Johannes Kuhn From chris.hegarty at oracle.com Fri Aug 23 14:39:54 2019 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Fri, 23 Aug 2019 15:39:54 +0100 Subject: Record Serialization - Update Message-ID: As a follow up to the message posted on amber-spec-experts [1], it turns out that supporting record serialization directly in the serialization runtime is relatively straight forward ( see draft specification changes below (*) ), and provides seamless migration from record-like classes to records ( and vica versa ). This is somewhat similar(ish) to how enums are supported, but without any change to the protocol format. Summary: - For a record to be serializable, it must `implements Serializable`. - The serial form of a record is it's state components. It is not customizable (*). - Serialization will only construct a record through its canonical constructor ( no other reflection / unsafe magic ). - The record components are, obviously, deserialized prior to the constructor being invoked. - Serialization will ignore pretty much all of the magic methods, if they appear in a record (**). - Record-like class to record migration is supported (and vice versa), since the stream format is not changed. - No need for "best match" constructor, just invoke the canonical constructor with the appropriate stream field values, or the the default value of the component's declared type if absent from the stream. - Apart from helpful warnings/errors, javac can almost forget about Serialization. Minimal specification changes are required to ObjectInputStream and ObjectOutputStream ( there will of course be equivalent wording in the separate Serialization spec [2]. (*) --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -139,6 +139,18 @@ * serialVersionUID field declarations are also ignored--all enum types have a * fixed serialVersionUID of 0L. * + *

When Preview Features are enabled... + * Record objects are serialized differently than ordinary serializable or + * externalizable objects. The serialized form of a record object is its state + * components ( in the same format as that of an ordinary object ). Like other + * serializable or externalizable objects, record objects can function as the + * targets of back references appearing subsequently in the serialization stream. + * The process by which record objects are serialized cannot be customized; any + * class-specific writeObject, writeReplace, and writeExternal, methods defined + * by record classes are ignored during serialization. Similarly, a + * serialPersistentFields field declaration is also ignored -- all record + * classes have a fixed serial form. The serialVersionUID may be set. + * *

Primitive data, excluding serializable fields and externalizable data, is * written to the ObjectOutputStream in block-data records. A block data record * is composed of a header and data. The block data header consists of a marker --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java * Similarly, any serialPersistentFields or serialVersionUID field declarations * are also ignored--all enum types have a fixed serialVersionUID of 0L. * + *

When Preview Features are enabled... + * Record objects are deserialized differently than ordinary serializable or + * externalizable objects. The serialized form of a record object is its state + * components ( in the same format as that of an ordinary object ). During + * deserialization, if the local class equivalent of the specified stream class + * descriptor {@linkplain Class#isRecord() is a record}, ObjectInputStream reads + * the record's state components from the stream; the record object is then + * constructed by invoking its canonical constructor passing the state + * components ( or the default value of the component's declared type if absent + * from the stream ). Like other serializable or externalizable objects, record + * objects can function as the targets of back references appearing subsequently + * in the serialization stream ( but there are clearly some limitations on the + * nature of the structures that can be represented, by virtue of the fact that + * the state components are deserialized prior to the invocation of the record + * constructor). The process by which record objects are deserialized cannot + * be customized: any class-specific readObject, readObjectNoData, readResolve, + * and readExternal, methods defined by record classes are ignored during + * deserialization. Similarly, a serialPersistentFields field declaration is + * also ignored -- all record classes have a fixed serial form, that is their + * state components. The serialVersionUID may be set. + * Wording will need to be agreed around how best to have the Serialization specification refer to records as a Preview Feature. (**) we want to allow `writeReplace` as an escape hatch, in case for example, a non-serializable type ends up as a component in a serializable record, or maybe if one wants to replace the serializable record with a proxy that, say, refers to a singleton value, or similar. -Chris. [1] https://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-July/001459.html [2] https://docs.oracle.com/en/java/javase/12/docs/specs/serialization/index.html From brian.goetz at oracle.com Fri Aug 23 15:01:51 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Aug 2019 11:01:51 -0400 Subject: Record Serialization - Update In-Reply-To: References: Message-ID: <91dc394e-b317-aa70-58c0-e498601f26ed@oracle.com> Thanks Chris.? This looks sensible.? A few questions inline. > - The serial form of a record is it's state components. It is not > customizable (*). Must the fields appear in the stream in the canonical order?? Or, are they serialized by name, and then we swizzle them back into the order desired by the canonical constructor? > - Serialization will ignore pretty much all of the magic methods, if > they appear in a record (**). What about serialVersionUID? What happens if the fields present are a superset, or subset, of the components in the canonical ctor? > - Record-like class to record migration is supported (and vice versa), > since the stream format is not changed. So, if a class C migrates to a record R, the serialized fields are just fed to the canonical ctor.? Must serialVersionUID match?? Or maybe, if at deserialization time it's a record, we just ignore svuid? > (**) we want to allow `writeReplace` as an escape hatch, in case for > example, a non-serializable type ends up as a component in a > serializable record, or maybe if one wants to replace the serializable > record with a proxy that, say, refers to a singleton value, or similar. This seems reasonable. From chris.hegarty at oracle.com Fri Aug 23 15:36:40 2019 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Fri, 23 Aug 2019 16:36:40 +0100 Subject: Record Serialization - Update In-Reply-To: <91dc394e-b317-aa70-58c0-e498601f26ed@oracle.com> References: <91dc394e-b317-aa70-58c0-e498601f26ed@oracle.com> Message-ID: <56C92233-5980-4F64-9D84-B9968945FA2C@oracle.com> > On 23 Aug 2019, at 16:01, Brian Goetz wrote: > > ... >> - The serial form of a record is it's state components. It is not >> customizable (*). > > Must the fields appear in the stream in the canonical order? Or, are they serialized by name, and then we swizzle them back into the order desired by the canonical constructor? The stream fields can appear in any order. They are matched to their respective constructor parameter by name. Since, the serial format doesn?t guarantee any ordering, and we wanna be able to go from serializable class Foo to record Foo. >> - Serialization will ignore pretty much all of the magic methods, if >> they appear in a record (**). > > What about serialVersionUID? The serialVersionUID is allowed and taken into consideration ( sorry, I accidentally omitted this important point ). Setting the serialVersionUID is required, to be able to migrate from a record-like class to a record. Since the former and latter will likely have different default serialVersionUID?s, if not explicitly set ( which one is encouraged to do ). > What happens if the fields present are a superset, or subset, of the components in the canonical ctor? For now, in the yet-to-be-polished prototype, we have: 1) All stream fields are deserialised ( regardless of whether they have a matching component ) - since one of them could be the target of a back reference. This is similar to other non-record data in the stream. 2) Stream fields are matched to their respective component, by name. 3) Components without a corresponding stream field have the appropriate type?s default value passed. The ctr can decide if the default value is allowable or not. >> - Record-like class to record migration is supported (and vice versa), >> since the stream format is not changed. > > So, if a class C migrates to a record R, the serialized fields are just fed to the canonical ctor. Right. ( class C migrated to record C - since the type?s name is present in the stream ) > Must serialVersionUID match? Currently, yes. > Or maybe, if at deserialization time it's a record, we just ignore svuid? That is one possibility, but explicitly declaring the suid seems like a good idea, doesn?t take much effort, is a little safer, and feels natural ( when one wants to preserve backward compatibility with a previous serial-form ). -Chris. From brian.goetz at oracle.com Fri Aug 23 15:43:30 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Aug 2019 11:43:30 -0400 Subject: Record Serialization - Update In-Reply-To: <56C92233-5980-4F64-9D84-B9968945FA2C@oracle.com> References: <91dc394e-b317-aa70-58c0-e498601f26ed@oracle.com> <56C92233-5980-4F64-9D84-B9968945FA2C@oracle.com> Message-ID: >> What about serialVersionUID? > The serialVersionUID is allowed and taken into consideration ( sorry, I accidentally omitted this important point ). Setting the serialVersionUID is required, to be able to migrate from a record-like class to a record. Since the former and latter will likely have different default serialVersionUID?s, if not explicitly set ( which one is encouraged to do ). So, what we'll need here is guidance for: ?- compiler writers, what to generate if no svuid is explicitly specified; ?- migrators, what to do when migrating from a class to a record. From chris.hegarty at oracle.com Fri Aug 23 15:57:52 2019 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Fri, 23 Aug 2019 16:57:52 +0100 Subject: Record Serialization - Update In-Reply-To: References: <91dc394e-b317-aa70-58c0-e498601f26ed@oracle.com> <56C92233-5980-4F64-9D84-B9968945FA2C@oracle.com> Message-ID: <8E0609B1-8D03-4AB2-A9F9-68F117788543@oracle.com> > On 23 Aug 2019, at 16:43, Brian Goetz wrote: > > >>> What about serialVersionUID? >> The serialVersionUID is allowed and taken into consideration ( sorry, I accidentally omitted this important point ). Setting the serialVersionUID is required, to be able to migrate from a record-like class to a record. Since the former and latter will likely have different default serialVersionUID?s, if not explicitly set ( which one is encouraged to do ). > > So, what we'll need here is guidance for: Yes, good point. Guidance will be needed. > - compiler writers, what to generate if no svuid is explicitly specified; Currently not setting an explicit suid is a lint warning, e.g. : $ cat Foo.java import java.io.*; public class Foo { record A () implements Serializable { } static class B implements Serializable { } } $ javac -Xlint:serial Foo.java Foo.java:4: warning: [serial] serializable class A has no definition of serialVersionUID record A () implements Serializable { } ^ Foo.java:6: warning: [serial] serializable class B has no definition of serialVersionUID static class B implements Serializable { } ^ 2 warnings , which seems reasonable, and consistent between classes and records. > - migrators, what to do when migrating from a class to a record. The guidance is: explicitly set a suid. Which should be familiar to anyone preserving serial compatibility when modifying a serializable class in the current version of their library and requiring interoperability with a previous version. I think I?m starting to see why you raised the question about whether or not checking the suid is really necessary! I guess it protects against accidental changes that could impact on previous serial forms. That said, an argument could be made that that check could be handled to the canonical constructor. -Chris. From gavin.bierman at oracle.com Fri Aug 23 21:25:05 2019 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 23 Aug 2019 22:25:05 +0100 Subject: Draft JLS spec for records Message-ID: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> A draft language spec for records is available at: http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.html This spec doesn?t yet discuss varargs records - to appear in the next draft. All comments welcomed! Thanks, Gavin From jordan at jordanzimmerman.com Sat Aug 24 13:15:01 2019 From: jordan at jordanzimmerman.com (Jordan Zimmerman) Date: Sat, 24 Aug 2019 08:15:01 -0500 Subject: Draft JLS spec for records Message-ID: <6EA2D3E7-53A8-46F6-84A0-FA2E71D1BD69@jordanzimmerman.com> Records (and the chain of features from switch expressions to pattern matching and deconstruction) will be as fundamental a change as Java 8's lambdas were. When the chain of features is complete, Java will likely have the best of breed version of this style of coding - except for one thing. Every other language I've surveyed has some method for mutating data carrier values. Now, I think it's the correct decision to make record fields final. But, why can't there be mutators (or a copy method) that returns a new record with the desired changes? I've already read discussion on various blogs/boards about this missing functionality in records. What this implies to me is that a) people want it dearly and b) when people want things various home grown solutions will be written. We can all think of many ways of solving this problem (each field has a corresponding mutator with the same name that returns a new record, a single copy method, etc.). Why not prevent confusion, disappointment and a plethora of incompatible mutating libraries now while this feature is still being developed? So, my plea is to please add some well defined way of creating modified copies of records so that they can be used in entity frameworks and the like. -Jordan From chris.hegarty at oracle.com Mon Aug 26 13:43:14 2019 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Mon, 26 Aug 2019 14:43:14 +0100 Subject: Draft JLS spec for records In-Reply-To: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> Message-ID: <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> Gavin, On 23/08/2019 22:25, Gavin Bierman wrote: > A draft language spec for records is available at: > > http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.html > ... "It is a compile-time error for a record header to declare a record component with the name clone, finalize, getClass, hashCode, notify, notifyAll, readObjectNoData, readResolve, serialPersistentFields, toString, wait, or writeReplace." TL;DR from a serialization POV, I think the above list is fine. -- Specifically on the restricted component names coming from serialization. The complete list of java.io.Serializable magic members: private void writeObject(java.io.ObjectOutputStream out) throws .. private void readObject(java.io.ObjectInputStream in) throws .. private void readObjectNoData() throws .. ANY-ACCESS-MODIFIER Object writeReplace() throws .. ANY-ACCESS-MODIFIER Object readResolve() throws .. private static final ObjectStreamField[] serialPersistentFields ANY-ACCESS-MODIFIER static final long serialVersionUID Ok, so you have all three no-args methods, looks fine, no possible collision between component accessor and the magic method. We could however decided to just ban both readObject and writeObject, as they could be confusing to the reader. But if it is just a minimal list of no-args methods, then what you have is fine. The listing of serialPersistentFields seems fine too, again possible collision with the record component field. serialVersionUID is static, so doesn't need to need to be listed. Ok. -- java.io.Externalizable is a lot less magic void readExternal?(ObjectInput in) void writeExternal?(ObjectOutput out) There are no no-args methods, good. If we decided to ban readObject and writeObject above, then we could consider banning these two component names also ( for the same reason we would ban the former ). -- Typo: there are two occurrences of "an record". -Chris. From chris.hegarty at oracle.com Mon Aug 26 14:03:27 2019 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Mon, 26 Aug 2019 15:03:27 +0100 Subject: Draft JLS spec for records In-Reply-To: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> Message-ID: Gavin, On 23/08/2019 22:25, Gavin Bierman wrote: > A draft language spec for records is available at: > > http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.h "8.10.4 Record Constructor Declarations Every record type R has a canonical constructor. This is a public constructor whose formal parameter list is identical to the record header of R." -- So for example, is this considered a canonical constructor? record R(int i, int j) { public R(int jjj, int iii) { this.i = jjj; this.j = iii; } } Does "formal parameter list" include the component name ( as well as the order and type )? -Chris. From vicente.romero at oracle.com Mon Aug 26 14:06:43 2019 From: vicente.romero at oracle.com (Vicente Romero) Date: Mon, 26 Aug 2019 10:06:43 -0400 Subject: Draft JLS spec for records In-Reply-To: References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> Message-ID: <3a128943-c046-587b-51d3-ab625823924f@oracle.com> On 8/26/19 10:03 AM, Chris Hegarty wrote: > Gavin, > > On 23/08/2019 22:25, Gavin Bierman wrote: >> A draft language spec for records is available at: >> >> http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.h >> > > "8.10.4 Record Constructor Declarations > > ?Every record type R has a canonical constructor. This is a public > ?constructor whose formal parameter list is identical to the record > ?header of R." > > -- > > So for example, is this considered a canonical constructor? > > ? record R(int i, int j) { > ??? public R(int jjj, int iii) { > ????? this.i = jjj; > ????? this.j = iii; > ??? } > ? } > > Does "formal parameter list" include the component name ( as well as > the order and type )? yes the canonical constructor has to match on component names too, > > -Chris. Vicente From brian.goetz at oracle.com Mon Aug 26 15:25:16 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Aug 2019 11:25:16 -0400 Subject: Draft JLS spec for records In-Reply-To: <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> Message-ID: <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> > The listing of serialPersistentFields seems fine too, again possible collision with the record component field. > > serialVersionUID is static, so doesn't need to need to be listed. Ok. You can?t have two fields with the same name, even if one is static and the other is not. So sVUID should stay on the list. From brian.goetz at oracle.com Mon Aug 26 15:26:54 2019 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Aug 2019 11:26:54 -0400 Subject: Draft JLS spec for records In-Reply-To: <3a128943-c046-587b-51d3-ab625823924f@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <3a128943-c046-587b-51d3-ab625823924f@oracle.com> Message-ID: <89E7287C-140F-4658-8A1B-454E1C42C5F4@oracle.com> >> >> Does "formal parameter list" include the component name ( as well as the order and type )? > > yes the canonical constructor has to match on component names too, I suspect this is one of the areas where the implementation and the spec don?t yet agree, so it is a good chance to discuss which should win :) Requiring the name match is pedantic but underscores the fact that the canonical ctor is about initializing the fields. I think its a reasonable restriction (one we can always lift later), but its also OK to not require it. Also, I?d like to say something about how the canonical ctor will have its parameter names reified always. From gavin.bierman at oracle.com Tue Aug 27 12:24:09 2019 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 27 Aug 2019 13:24:09 +0100 Subject: Draft JLS spec for records In-Reply-To: <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> Message-ID: <201D8606-FA93-4C1E-8E5D-EB1C4BDA6952@oracle.com> Thanks Chris! > On 26 Aug 2019, at 14:43, Chris Hegarty wrote: > > Gavin, > > On 23/08/2019 22:25, Gavin Bierman wrote: >> A draft language spec for records is available at: >> http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.html >> ... > > > "It is a compile-time error for a record header to declare a record > component with the name clone, finalize, getClass, hashCode, notify, > notifyAll, readObjectNoData, readResolve, serialPersistentFields, > toString, wait, or writeReplace." > > TL;DR from a serialization POV, I think the above list is fine. > > -- > > Specifically on the restricted component names coming from serialization. > > The complete list of java.io.Serializable magic members: > > private void writeObject(java.io.ObjectOutputStream out) throws .. > private void readObject(java.io.ObjectInputStream in) throws .. > private void readObjectNoData() throws .. > ANY-ACCESS-MODIFIER Object writeReplace() throws .. > ANY-ACCESS-MODIFIER Object readResolve() throws .. > > private static final ObjectStreamField[] serialPersistentFields > ANY-ACCESS-MODIFIER static final long serialVersionUID > > Ok, so you have all three no-args methods, looks fine, no possible collision between component accessor and the magic method. We could however decided to just ban both readObject and writeObject, as they could be confusing to the reader. But if it is just a minimal list of no-args methods, then what you have is fine. > > The listing of serialPersistentFields seems fine too, again possible collision with the record component field. > > serialVersionUID is static, so doesn't need to need to be listed. Ok. > > > -- > > java.io.Externalizable is a lot less magic > > void readExternal?(ObjectInput in) > void writeExternal?(ObjectOutput out) > > There are no no-args methods, good. If we decided to ban readObject and writeObject above, then we could consider banning these two component names also ( for the same reason we would ban the former ). > > -- > > Typo: there are two occurrences of "an record". > > -Chris. From gavin.bierman at oracle.com Tue Aug 27 12:39:41 2019 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 27 Aug 2019 13:39:41 +0100 Subject: Draft JLS spec for records In-Reply-To: <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> Message-ID: <4B10972F-7844-4730-A353-FF672DD978C7@oracle.com> Added. > On 26 Aug 2019, at 16:25, Brian Goetz wrote: > >> The listing of serialPersistentFields seems fine too, again possible collision with the record component field. >> >> serialVersionUID is static, so doesn't need to need to be listed. Ok. > > You can?t have two fields with the same name, even if one is static and the other is not. So sVUID should stay on the list. > From laksvij at hawk.iit.edu Tue Aug 27 13:32:55 2019 From: laksvij at hawk.iit.edu (Vijay Lakshminarayanan) Date: Tue, 27 Aug 2019 19:02:55 +0530 Subject: Draft JLS spec for records In-Reply-To: <4B10972F-7844-4730-A353-FF672DD978C7@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> <4B10972F-7844-4730-A353-FF672DD978C7@oracle.com> Message-ID: Hi Gavin >From the spec it seems there may be only one annotation against a RecordComponent or against a constructor. >From 8.10.1 Record Header > RecordComponent: {Annotation} UnannType Identifier and from 8.10.2 > CompactConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody Wouldn't the following be legal? record NonNullName(@NotNull @NotModified StringBuilder name) { @JsonCreator @XmlCreator @BsonCreator public NonNegativePoint { ... } } Thanks Vijay On Tue, Aug 27, 2019 at 6:12 PM Gavin Bierman wrote: > > Added. > > > On 26 Aug 2019, at 16:25, Brian Goetz wrote: > > > >> The listing of serialPersistentFields seems fine too, again possible collision with the record component field. > >> > >> serialVersionUID is static, so doesn't need to need to be listed. Ok. > > > > You can?t have two fields with the same name, even if one is static and the other is not. So sVUID should stay on the list. > > > From gavin.bierman at oracle.com Fri Aug 30 18:11:22 2019 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 30 Aug 2019 19:11:22 +0100 Subject: Draft JLS spec for sealed types In-Reply-To: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> Message-ID: <6EE17B9C-B7D2-46D9-8442-AD9E9D9815D5@oracle.com> A draft language spec for sealed types is available at: http://cr.openjdk.java.net/~gbierman/jep360/jep360-20190830/specs/sealed-types-jls.html This spec doesn?t yet contain details on binary compatibility (Chapter 13) - to appear in the next draft. All comments welcomed! Thanks, Gavin From forax at univ-mlv.fr Fri Aug 30 18:53:24 2019 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 30 Aug 2019 20:53:24 +0200 (CEST) Subject: Draft JLS spec for records In-Reply-To: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> Message-ID: <129906317.2061938.1567191204608.JavaMail.zimbra@u-pem.fr> Hi all, iwas fianlly enable to have the time to read the spec, yay ! In section 8.10.3:: - what if the record implements an interface with a default method that provides an implementation for a getter. Currently this implementation is ignored but i don't know if it's surprising or not. Maybe we should ask explicitly for the user providing an implementation of the getter in that case ? So this example should not compile ? interface I { default int x() { System.out.println("foo"); return -1; } } record Foo(int x) implements I { } In section 8.10.4: - why the constructor has to be public, if the record is used inside the class, i don't want someone to be able to instantiate it by reflection. public class PublicConstructorProblem { private record Foo(int x) { } public static Object foo() { return new Foo(42); } public static void main(String[] args) throws Exception { Object o = foo(); Object o2 = o.getClass().getConstructor(int.class).newInstance(43); System.out.println(o2); } } - a canonical constructor can not have throws clause (from the text of the section) but the grammar in 8.10.2 the CompactConstructor declaration can have a throw clause ? - the allowed modifier for a compact constructor should be explicitly listed By example, i believe strictfp or synchronized should be allowed public record StrictFP() { public strictfp StrictFP { } } but the current prototype rejects that code. regards, R?mi > De: "Gavin Bierman" > ?: "amber-spec-experts" > Envoy?: Vendredi 23 Ao?t 2019 23:25:05 > Objet: Draft JLS spec for records > A draft language spec for records is available at: > [ > http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.html > | > http://cr.openjdk.java.net/~gbierman/8222777/8222777-20190823/specs/records-jls.html > ] > This spec doesn?t yet discuss varargs records - to appear in the next draft. > All comments welcomed! > Thanks, > Gavin From anthonyv.be at outlook.com Fri Aug 30 19:26:22 2019 From: anthonyv.be at outlook.com (Anthony Vanelverdinghe) Date: Fri, 30 Aug 2019 19:26:22 +0000 Subject: Draft JLS spec for records In-Reply-To: References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> <4B10972F-7844-4730-A353-FF672DD978C7@oracle.com> Message-ID: Hi Vijay In the JLS grammar notation [1], {x} denotes zero or more occurrences of x, so there are no constraints on the number of annotations. [1] https://docs.oracle.com/javase/specs/jls/se12/html/jls-2.html#jls-2.4 Kind regards, Anthony On 27/08/2019 15:32, Vijay Lakshminarayanan wrote: > Hi Gavin > > From the spec it seems there may be only one annotation against a > RecordComponent or against a constructor. > > From 8.10.1 Record Header > >> RecordComponent: {Annotation} UnannType Identifier > and from 8.10.2 > >> CompactConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody > Wouldn't the following be legal? > > record NonNullName(@NotNull @NotModified StringBuilder name) { > @JsonCreator @XmlCreator @BsonCreator public NonNegativePoint { ... } > } > > Thanks > Vijay > > On Tue, Aug 27, 2019 at 6:12 PM Gavin Bierman wrote: >> Added. >> >>> On 26 Aug 2019, at 16:25, Brian Goetz wrote: >>> >>>> The listing of serialPersistentFields seems fine too, again possible collision with the record component field. >>>> >>>> serialVersionUID is static, so doesn't need to need to be listed. Ok. >>> You can?t have two fields with the same name, even if one is static and the other is not. So sVUID should stay on the list. >>> From laksvij at hawk.iit.edu Sat Aug 31 01:44:45 2019 From: laksvij at hawk.iit.edu (Vijay Lakshminarayanan) Date: Sat, 31 Aug 2019 07:14:45 +0530 Subject: Draft JLS spec for records In-Reply-To: References: <146C0891-97B3-4DBD-AA10-CCD485357BA2@oracle.com> <016e6adf-b4a4-8b24-49ff-2a6a1e98cc3b@oracle.com> <1231E945-796F-413D-BBA3-0893B8C77F25@oracle.com> <4B10972F-7844-4730-A353-FF672DD978C7@oracle.com> Message-ID: I see. Thanks, Anthony, for clarifying. Thanks Vijay On Sat, Aug 31, 2019 at 12:56 AM Anthony Vanelverdinghe wrote: > > Hi Vijay > > In the JLS grammar notation [1], {x} denotes zero or more occurrences of > x, so there are no constraints on the number of annotations. > > [1] https://docs.oracle.com/javase/specs/jls/se12/html/jls-2.html#jls-2.4 > > Kind regards, > Anthony > > On 27/08/2019 15:32, Vijay Lakshminarayanan wrote: > > Hi Gavin > > > > From the spec it seems there may be only one annotation against a > > RecordComponent or against a constructor. > > > > From 8.10.1 Record Header > > > >> RecordComponent: {Annotation} UnannType Identifier > > and from 8.10.2 > > > >> CompactConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody > > Wouldn't the following be legal? > > > > record NonNullName(@NotNull @NotModified StringBuilder name) { > > @JsonCreator @XmlCreator @BsonCreator public NonNegativePoint { ... } > > } > > > > Thanks > > Vijay > > > > On Tue, Aug 27, 2019 at 6:12 PM Gavin Bierman wrote: > >> Added. > >> > >>> On 26 Aug 2019, at 16:25, Brian Goetz wrote: > >>> > >>>> The listing of serialPersistentFields seems fine too, again possible collision with the record component field. > >>>> > >>>> serialVersionUID is static, so doesn't need to need to be listed. Ok. > >>> You can?t have two fields with the same name, even if one is static and the other is not. So sVUID should stay on the list. > >>>