Java Feature Spotlight: Text Blocks
infoq.com
infoq.com
It is not simply 'here's a new language feature'. I find it quite informative regarding what, why, how and why not etc. His articles explain what went into the decision making. Something on the lines of discussions in Effective Java by Joshua Bloch.
His article on records: https://www.infoq.com/articles/java-14-feature-spotlight/
Loom and Kotlin coroutines are not the same though. Loom will bring real fibres to the JVM, which maybe Kotlin coroutines can use. Kotlin coroutines should also benefit from Loom.
Even stripping trailing whitespace is... risky. Why is the language trying to decide what whitespace is significant? Or even trying to divine the mind of the developer at all?
I didn't realize there was a raw string proposal in Java 12 using backticks (bleh). By now I'm pretty used to languages that use double-quotes for interpreted strings (including escape sequences, at a minimum) and single-quotes for raw strings. This is an incredibly useful feature. Of course, Java uses single-quotes for character literals so that's out, otherwise I'd like to see """ for interpreted and ''' for raw.
Anyway, let me point out just one reason why this is terrible: source control. Consider:
String s1 = """
<outer>
<inner>foo</inner>
</outer>
"""";
Now change this to: String s1 = """
<outer>
<inner>foo</inner>
</outer>
"""";
It's only one line change but you have in fact changed what the previous lines do.And of course (which the article mentions).... tabs!
I really want the option of pasting text into a block like this and knowing that's what I'll get. I don't want to process it usually (eg escaping characters).
String s1 = """<outer>
<inner>foo</inner>
</outer>""";
is ugly.I’m personally a fan of having an easily-accessible method to call. That way you get indented text blocks and no “magic” formatting (at least, you explicitly opt-in to the magic formatting).
This type of remark is the height of arrogance. The whole process of designing text blocks was in the open. It was a preview feature for two releases (three if you count the raw string proposal in JDK 12) and thousands of people gave their feedback what they wanted to be changed. That your preferred version wasn't chosen is not the result of "the unsubstantiated aesthetic view of a handful of key people", but of this process.
Scala has stripMargin, integrated into IntelliJ to explicitly indicate how to dedent the text block. Kotlin has trimIndent.
Anything that takes this long to explain should not be a core language feature.
-----String s1 = """
-----<outer>
----- <inner/>
-----</outer>
-----""";
this works fine when there is no final line end too, as the closing quotes are not used for whitespace significance.Just seems more obvious/clear.
Well, I'll withhold judgement somewhat until I eventually use this feature for a little while, but based on the description this is simply too complex, which is disappointing. Who exactly was asking for the "magical rectangle" approach, anyway? Or incidental vs essential indentation? They did too much. And given how long people have been waiting for simple multi-line strings, and how many other languages already have them, I'm not sure how this got messed up.
This has some implications. For example, I can imagine someone editing a text block and inadvertently using a different indentation type (tabs/spaces) from the other existing lines, which would then change their program in a non-obvious way. I'm myself an author of a programming language with a non-standard approach to whitespace (called Muon), so this is a topic I've thought about quite a bit. Interesting that the Java team considers this an acceptable tradeoff. I'm looking forward to seeing how this plays out in practice as a data point.
char* text = "hello" " dear"
" sir";
This is great, because it doesn't make leading whitespace significant on the lines which the text block spans.The Java approach, which seems similar to the Python approach, looks really silly when you are defining a literal in a nested scope;
def hello():
def world():
text = """hello
world"""
print textI think you might want to go and reread the article, they are using "determining lines" to remove leading whitespace. I think I would find this approach much more readable than Cs
text = """hello
world"""
will contain the intendation before world, while in Java: String text = """hello
world"""
will be equivalent to "hello\nworld".The closing delimiter can, however, be on the same line as the final content (in order to suppress the trailing newline for instance).
var text = """
hello
world""";
will not have any indentation, as the common indentation is stripped from all lines.I'm not sure you can really compare them. They're two different features with fairly different uses, it's just that one or two of those uses somewhat overlap.
void m() {
System.out.println("""
hello
world
""");
}Python also allows that.
The semantics are completely different and not at all incompatible (even though Java doesn't support implicit concatenation of sibling literals), string blocks means you don't have to bother with explicit newlines and are way more convenient for long blocks of embedded text, especially when newlines are significant and frequent (e.g. wrapped paragraphs).
Java also seems to be getting a slightly more flexible version of Swift's processing, so it will automatically dedent based on the indentation of the line containing the trailing delimiter, or the least indented line of content within the block. Meaning you can write
text = """
hello
world"""
and get "hello\nworld". Though it is apparently forbidden to have non-whitespace content on the same line as the opening delimiter:> The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator.
Text content can be indented less than the trailing delimiter, which I understand is an error in Swift
String html = """
..............<html>
.............. <body>
.............. <p>Hello, world</p>
.............. </body>
..............</html>
.............. """;
The trailing delimiter can also be on the same line as the final content, which suppresses the final (new)line: """
line 1
line 2
line 3"""
translates to "line 1\nline 2\nline 3"
whereas """
line 1
line 2
line 3
"""
translates to "line 1\nline 2\nline 3\n"
I believe this requires escaping the newline in Swift instead (nb: according to the JEP this would also work in Java).As I wrote in my comment, yes.
Lots of cross pollination.
>> While Java could not adopt the Swift approach wholesale because of existing language constraints, the Java approach took as much inspiration from the good work that the Swift community did as we could -- and left room to take more in the future.
(For example they have major limitations in Electron, which is one place where having threads would be very convenient. The current best practice is to spin up an entire new hidden browserwindow instead which is frankly insane.)
Apache, for example, supports a ton of Java libraries for which there's little competition, certainly not well distributed across language ecosystems. PDFBox, Lucene, etc.
When you're in the nitty gritty of automating business processes, that sort of thing is invaluable. For CRUD-style web services, any language will do the trick.
(I say this as not a big fan of Java the language)
Python also has a similarly wide ecosystem, and does better specifically in some regions than any other language, like ML, but a different set of focus areas. It's probably the other top contender for business-process automation, but big enterprises prefer OOP/static typing for typical work.
C# probably comes close in many areas, but there's still a split between the .net core and not-core ecosystems for the long tail of libraries, and no open-source powerhouse quite like Apache in the mix.
(C/C++ might fall here somewhere too, but I don't know nearly enough about those language ecosystems to comment)
If you'll forgive a light trolling: how about ahead-of-time compilation, allocating small objects on the stack, UTF-8, compile-time asserts, or SIMD support?
(Was going to include await/async, but then I remembered EA Async.)
I realise this is a little off topic, but the JVM isn't without limitations. (Or perhaps all my examples are wrong - disproof welcome!)
You can specify UTF-8 as the default charset (unless you mean for identifiers?) and Grall does exist for AOT.
For Async/await, I believe a much better approach is available with Quasar today or with Project Loom soon.
SIMD is an issue though if you need that.
See Project Panama for the encompassing project: https://openjdk.java.net/projects/panama/
SIMD: https://openjdk.java.net/jeps/338 (to be merged soon), plus, of course, auto vectorisation, which is already in.
Allocating small objects on the stack: this already happens automatically through escape analysis, but more generally (and more importantly) arrays-of-structs: http://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-backg...
UTF-8: What do you mean? Java has supported UTF-8 for a loooong long time. Even the source files support Unicode with UTF-8 encoding.
compile-time asserts: Not sure what you mean. But there's a rich compile-time plugin mechanism: https://www.baeldung.com/java-annotation-processing-builder and even pluggable type systems: https://checkerframework.org/manual/
Async/await: how about lightweight threads, instead? https://wiki.openjdk.java.net/display/loom/
We have a Lucene binding, too. It's called Elasticsearch. :)
The goal of the JVM is "write once, run anywhere". v8 achieves the same, but for languages that compile to Javascript. E.g. Dart, Elm, Typescript, CoffeeScript, ClojureScript etc.
I think the real choice to be made is ecosystem. There are languages to suit every programming style in either camp. The availability and quality of packages in the Java ecosystem is higher IMO (use Spring and it just works).
Node has non-blocking I/O by default; it's possible to implement the same in Java, but you have to make a conscious decision to do so. Java has threads, but Node has workers. Java can have a longer startup time and is better suited for servers with high uptime, Node starts up quick and is easier to use in on demand situations etc.
I don't really know why you would, but there is RingoJS if you want to run Javascript on the JVM for some reason.
[1]: https://blogs.oracle.com/javamagazine/java-flight-recorder-a...
That the Java language is slow to evolve while the Java platform evolves quickly has been one of the central strategies of Java since inception. In fact, James Gosling wrote back in 1997 how Java should only adopt language features loooong after they've been tried in other languages. It's worked very, very well. There's obviously a portion of users (<10%) who prefer a faster-moving, feature-rich language, and they have good options on the Java platform, but the Java language philosophy has been more attractive to a much larger group of developers.
What is more interesting is not which features Java has chosen to adopt, but which ones it hasn't and won't: yes to lightweight threads but no to async/await; yes to pattern-matching but no to extension methods.
* I don't think Java still has string interpolation.
* List, Map literals are not a thing Java has either.
* Value types aren't in yet.
* If as expressions. You have to use ternary.
These aren't complaints. I know what I'm getting with Java. No hate.
Switch expressions can do what if expressions do.
Extension methods
Async/await -- Java is going with lightweight threads.
I'm looking at Scala and C++
Strings and automatic boxing come to mind.
A) Throwing the kitchen sink in and making mistakes.
B) Changing anything that already works.
There are very few languages that continue to improve and provide such long term stability. More often than not more feature full languages end up forcing me to re-test or rewrite sections of my old code base for forwards compatibility.
It's also useful for small amounts of structured content e.g. if you don't yet need to rely on a full-blown templating system for your HTML, or to POST small amounts of static JSON or somesuch.
> You lose syntax formatting if it's structured content
Jetbrains's IDEs do content-sniffing and formatting and coloration of embedded content (though it breaks down somewhat quickly if you start doing string formatting within the block).
It's more convenient and safer to put SQL (or other text) inline next to the code that uses it rather than "some other file" that can fall out of sync.
1. Records are already there AFAIK
2. Structs (Value types) will come as part of Valhalla
3. Records are the impl of dataclasses
4. Pass by value is already there for primitive types. Valhalla might bring that to structs also.
Java is also getting a lot of good stuff like coroutines(Loom), and Panama(seamless FFI).
> Records are the impl of dataclasses
This is ofc backwards/reverse of any sane logic that would start with value types and after invent references (eg. in this case probably would've mean introducing a new composed-primitive type)... but then again, it's Java, at least they got the strings being immutable part right from the start.
I'm guessing that they'll land in Spring or Fall 2021, but that's just an outsider's guess. The really important cutoff is Spring 2022, when Java 18 is intended to be an LTS version.
If you think about it - the feature may change significantly or be even removed - it makes sense to only support a preview features in one java version.
Providing backward compatibility for preview features is simply not intended.
This is nuts to me how behind Java is. Is it only due to the backward compatibility? Or is there some crazy red tapping every time something is added?
They are especially bad with exotic operators like postgres' JSONB columns. I ended up ripping jooq out of an app over this.
There's an argument that they bring type safety to SQL, but Intellij actually validates your string literal SQL against the schema, so the benefit is marginal.
Additionally Java has a longer history than most of backwards compatible software that contributes to a higher burden of testing before making changes to the core language.
Finally there is the community itself which favours high performance and reliability and prefers where possible to make use of libraries rather than change the language or runtime itself.
That isn't to say Java isn't evolving or that the JVM itself is stagnant.
The introduction of the Java module system allowed the JRE itself to become a much lighter dependency. Things like Project Loom seek to bring Erlang-like fiber/green thread support to the JVM and will probably land in a release soon.
Also the release cadence has been changed so new releases are now landing much more frequently which is increasing the pace of development.
The virtual machine and stdlib continue to see innovation, while the ease of implementing JVM languages like Scala and Kotlin mean that Java can let these new languages innovate, and pick the best features when they're proven (with improved performance normally too).
You can see this with Lambdas, and now with Records and pattern matching
OTOH, Java programmers look in astonishment at how far behind most other platforms are in GC, compilation, and observability.
- Almost every feature added makes every feature that comes after it more expensive to add
What makes these arguments specific to Java?