I think having multiple processors is very smart exactly due to the outlined reason - making sure the interpolated result is safe for the system that will consume that string, be it a query language or json or something else.
f"" vs STR."" is really a quite minimal difference, and I think worth it. Having the "." operator here mean "interpolate RHS using LHS" seems a nice balance between terseness and explicitness.
As for "\{" being a divergent choice, frankly who cares.
var $ = STR;
var $h = new HtmlTemplateThing();
var $j = new JSONTemplateThing();
var name = "John";
var s = $."Hello \{name}";
// HTML escaped
var h = $h."<b>\{name}</b>";
// JSON escaped
var j = $j."""
{ "name" : "\{name}" }
"""; // JSON escaped
I love this one:
var $db = new DBConnectionTemplateThing();
// SQL Escaped and processed
PreparedStatement st = $db."select * from table where name = '\{name}'";
Or:
var $x = new XMLTemplateThing();
// XML escaped and parsed
Document d = $x."<xml>\{name}</xml>";
This is pretty powerful. They selected \{ for the start of expressions because of the legacy of Java Expression Language (EL) which came from JSP and JSF.
“\{“ reminds me of Swift’s “\(“. So I wouldn’t think it’s even that divergent. And Java devs are already used to backslashes peppering strings (me included).
But the security problems that string interpolation introduces are one of the reasons why Java has decided to reject string interpolation as a language feature in favour of string templates (you can use string templates for interpolation as a special case of a template processor, but that's not the feature's raison d'être). The reason for string templates is to improve the convenience and (very poor) security of string manipulation.
The JEP explains why Java is getting string templates and is not getting string interpolation (as a language feature).
I wasn't able to tell from this document, IMO since they are focused on safety as a reason for their templates approach, they should include some defaults for common scenarios if they'd liked this to gain wider usage. For example that QueryBuilder they used for SQL would be useful.
Too often I see experienced developers using string interpolation for SQL and not realising it's open to injection. I feel the document's intentions are good but because the implementation looks so verbose they should make it as frictionless as possible to gain wider adoption. Else all that's going to happen is people will only ever use STR.
I think the whole thing is pretty useless, but I guess the point is that with your own template format you could write a string template format that fully escapes sql variables, for instance.
Yes, we already have that problem solved with JPQL or even just plain prepared statement but that’s never stopped anyone…
And yes, you could also write a message formatter the current Java that does that…
Except then it would still be absolutely trivial for people to write their own string-interpolating template processors, which they would definitely do, and so we figured we might as well offer a standard one (that also offers better performance than hand-rolled ones). It is difficult to find the right balance between educating people to do "the right thing" as we see it and rolling with the recognition that they not always will. I wish we could say there's some formula we apply, but we need to exercise such judgment with lots of features and it's never a trivial decision. In this case we figured that if we make the thing people will want to do anyway no easier than the "right thing", that's probably as good as we can do, all things considered.
Unpleasantly verbose, especially compared to Kotlin. They'd have come up with a much better approach if they had to add it to their list of current implementations.
C# $"{x} plus {y} equals {x + y}"
Visual Basic $"{x} plus {y} equals {x + y}"
Python f"{x} plus {y} equals {x + y}"
Scala s"$x plus $y equals ${x + y}"
Groovy "$x plus $y equals ${x + y}"
Kotlin "$x plus $y equals ${x + y}"
JavaScript `${x} plus ${y} equals ${x + y}`
Ruby "#{x} plus #{y} equals #{x + y}"
Swift "\(x) plus \(y) equals \(x + y)"
Java STR."\{x} plus \{y} equals \{x + y}"
For the 'injection' case, they could define an annotation which stops interpolation on a provided parameter, and provide a more verbose method to handle templated interpolation correctly.
A sample syntax by JetBrains is below. [0] In future, this could be leveraged to block/warn against dangerous interpolations
@Language("SQL") // This adds SQL highlighting, encouraging developers to use it
String query = "SELECT * FROM Person p WHERE p.last_name = '${name}'" // future warning/error
It's not better though. The STR template processor behaves the exact same way as all the other examples, and it's the one that all the inexperienced devs prone to this kind of injection attack will use.
Note, that Java had to maintain backward compatibility, so they couldn't inject any new syntax into existing string literals. So, they had a choice between:
1. Introducing syntax for new kind of string literals (like JS did)
2. Make some kind of explicit function/constructor call (like they ended up doing).
Advantage of 2 is that it doesn't require any new support from IDEs/parsers/code quality tools/etc. It is slightly more verbose, but not as verbose as some other Java syntax. IMO they've made the right decision.
But the solution does need support from IDEs and parsers. STR."\{STR."\{a}"}" is a valid expression, now, that uses a variable, and looks nothing like any previously valid expression. Given that it is an entirely new kind of expression, it's also unclear why they would choose \{ over { or need the dot. Is there some reason that they couldn't have chosen STR"{a}"?
Can I call out JavaScript for using backticks for a clean distinction between templated and untemplated strings:
- no strange leading character(s) (per C#, VB, Python, Scala, and now Java)
- templated strings are actually distinguished, so don't have to worry about accidentally running into template literals in normal strings (unlike Groovy, Kotlin, Ruby)
- the distinguishing mark appears at the end as well as the beginning, for slightly better legibility.
- Swift is quite nice in that the backslash is already an escape character for normal strings, so no issue with accidental use, but {} would have been a better choice than () as the former are rarer in normal strings.
> no strange leading character(s) (per C#, VB, Python, Scala, and now Java)
There are no strange leading characters in Java, either. A template expression is a special case of an instance method call (it's a call to the `process` method on the template processor), and the "leading characters" are the same ones Java already requires for all instance calls. There's nothing strange about them; what is new (and therefore possibly strange) is the use of `"` for the method call instead of a method name followed by `(`. In other words, the new syntax Java added is what follows the dot in the method call to `process`; everything up to and including the dot is normal Java syntax. This is important to emphasise that a template string expression is an a method call that may (although normally shouldn't) even have a side effect.
> templated strings are actually distinguished, so don't have to worry about accidentally running into template literals in normal strings
Same here. `\{` inside a string remains a syntax error, as it always has been. Also, Java is typed (and the template expression requires a receiver of type StringTemplate.Processor) and there cannot be any ambiguity, and so it's unnecessary to introduce yet another punctuation mark. JS needed further distinguishing syntax because it is untyped.
But the whole point of the JEP is to offer a different and better feature than string interpolation because the JEP claims that string interpolation should be avoided. Saying that using it for string interpolation is more verbose than the very thing the JEP tried to avoid doing is missing the whole point. The idea is that in the most important cases you should avoid the `STR` processor in favour of a JSON, HTML, or SQL processor.
You can agree or disagree with the JEP's goal, but pointing out that the JEP manages to discourage the very thing that it sets out to discourage merely acknowledges that the goal is met.
Both the syntax and the semantics are awful. They are overthinking it.
Processor should not require to allocate a list of objects do to string templating. Only STR and FMT get good perf because they are specialized by the compiler.
> Only STR and FMT get good perf because they are specialized by the compiler.
Sort of. The specialisation happens for any user of MethodHandle.invokeExact, which is what those processors use (i.e. the compiler doesn't specifically know about STR and FMT), it's just that the MethodHandle API for template processors is not yet public. I expect it will be made public later on.
Does not allocate a list of objects in C# :) (there is an intermediate pooled buffer, which is then collected to a newly allocated string of an exact size)
What's Kotlin going to do about that? Will they adopt their syntax or make it use the same API internally?
This seems just like another feature of the JVM mother language that diverges from the implementation of other often-used JVM languages like Kotlin (thinking of nullability, type reification and structured concurrency and record types). It will be interesting to see what happens when Kotlin is continued to be pushed as the language for Android etc. while Java is being used by the majority of JVM devs.
Inability to reasonably operate an API that has that StringTemplate type somewhere in a method signature would be a dramatic change of direction for kotlin. I'd be surprised if they didn't already have something in the pipeline to support \{it} strings on the syntax level, it's not like they pretend java-side progress didn't exist.
The existing ${it} syntax is tied to string on the output, so it can't be repurposed. The java approach on the other hand keeps the output generic, so that it would also keep the door open for similar not-string compatibilities in off-JVM kotlin implementations. Whereas the ${it} the syntax kotlin already has couldn't do that. An approach like changing its semantics to define something that isn't a string when it exists at a StringTemplate.Processor call site would be terrible I think, php-grade terrible. At least as long as they don't also introduce that formatter-dot-template syntax which I'd consider a pointless redundancy with what kotlin already has: formatter{template} is already possible, expect for a way to write a template literal that resolves to something other than string.
What I certainly wouldn't expect is anything reminiscent of "automatically imported into every Java source file".
It's certainly a very visible language and one often talked about, but if you look at Google trends for Java versus kotlin you're looking at about 10% of the adoption that Java has or less.
The languages don't have much surface area of features to search about. Far more google queries will be about API than about syntax. I wouldn't be surprised if the browser histories of kotlin developers, on average, contained far more occurrences of "google.+java" than of "google.+kotlin".
f"" vs STR."" is really a quite minimal difference, and I think worth it. Having the "." operator here mean "interpolate RHS using LHS" seems a nice balance between terseness and explicitness.
As for "\{" being a divergent choice, frankly who cares.
Doesn't look that terrible to me.
https://developer.apple.com/documentation/swift/stringinterp...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I used it to support a LiveView implementation for Java: https://github.com/floodfx/undead
Sure the syntax isn't "pretty" but understandable and you get used to it like anything else.
The JEP explains why Java is getting string templates and is not getting string interpolation (as a language feature).
It's the reason they didn't chose "${" like many other languages. Because that's a valid sequence in a string literal.
`\{` as an interpolation leader would make sense if untagged string literals became implicit STR templates, but that's not the case.
Too often I see experienced developers using string interpolation for SQL and not realising it's open to injection. I feel the document's intentions are good but because the implementation looks so verbose they should make it as frictionless as possible to gain wider adoption. Else all that's going to happen is people will only ever use STR.
That said, I wonder how often people are writing queries out by hand like that these days vs using ORMs or jOOQ.
Yes, we already have that problem solved with JPQL or even just plain prepared statement but that’s never stopped anyone…
And yes, you could also write a message formatter the current Java that does that…
> String interpolation is dangerous > Can we do better?
For the 'injection' case, they could define an annotation which stops interpolation on a provided parameter, and provide a more verbose method to handle templated interpolation correctly.
A sample syntax by JetBrains is below. [0] In future, this could be leveraged to block/warn against dangerous interpolations
[0] https://www.jetbrains.com/help/idea/using-language-injection...1. Introducing syntax for new kind of string literals (like JS did)
2. Make some kind of explicit function/constructor call (like they ended up doing).
Advantage of 2 is that it doesn't require any new support from IDEs/parsers/code quality tools/etc. It is slightly more verbose, but not as verbose as some other Java syntax. IMO they've made the right decision.
Furthermore, they went on to use a printf-style formatting spec prefixing the template leader.
> Advantage of 2 is that it doesn't require any new support from IDEs/parsers/code quality tools/etc.
Of course it does, the sequence "IDENTIFIER, PERIOD, STRING LITERAL" is currently invalid, anything beyond a tokeniser should choke on it.
Deleted Comment
- no strange leading character(s) (per C#, VB, Python, Scala, and now Java)
- templated strings are actually distinguished, so don't have to worry about accidentally running into template literals in normal strings (unlike Groovy, Kotlin, Ruby)
- the distinguishing mark appears at the end as well as the beginning, for slightly better legibility.
- Swift is quite nice in that the backslash is already an escape character for normal strings, so no issue with accidental use, but {} would have been a better choice than () as the former are rarer in normal strings.
There are no strange leading characters in Java, either. A template expression is a special case of an instance method call (it's a call to the `process` method on the template processor), and the "leading characters" are the same ones Java already requires for all instance calls. There's nothing strange about them; what is new (and therefore possibly strange) is the use of `"` for the method call instead of a method name followed by `(`. In other words, the new syntax Java added is what follows the dot in the method call to `process`; everything up to and including the dot is normal Java syntax. This is important to emphasise that a template string expression is an a method call that may (although normally shouldn't) even have a side effect.
> templated strings are actually distinguished, so don't have to worry about accidentally running into template literals in normal strings
Same here. `\{` inside a string remains a syntax error, as it always has been. Also, Java is typed (and the template expression requires a receiver of type StringTemplate.Processor) and there cannot be any ambiguity, and so it's unnecessary to introduce yet another punctuation mark. JS needed further distinguishing syntax because it is untyped.
You can agree or disagree with the JEP's goal, but pointing out that the JEP manages to discourage the very thing that it sets out to discourage merely acknowledges that the goal is met.
That idea is evidently idiotic as demonstrated by javascript which has this exact feature, in a better form (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...): the most common template literals by far are untagged.
Dead Comment
Processor should not require to allocate a list of objects do to string templating. Only STR and FMT get good perf because they are specialized by the compiler.
Sort of. The specialisation happens for any user of MethodHandle.invokeExact, which is what those processors use (i.e. the compiler doesn't specifically know about STR and FMT), it's just that the MethodHandle API for template processors is not yet public. I expect it will be made public later on.
This seems just like another feature of the JVM mother language that diverges from the implementation of other often-used JVM languages like Kotlin (thinking of nullability, type reification and structured concurrency and record types). It will be interesting to see what happens when Kotlin is continued to be pushed as the language for Android etc. while Java is being used by the majority of JVM devs.
The existing ${it} syntax is tied to string on the output, so it can't be repurposed. The java approach on the other hand keeps the output generic, so that it would also keep the door open for similar not-string compatibilities in off-JVM kotlin implementations. Whereas the ${it} the syntax kotlin already has couldn't do that. An approach like changing its semantics to define something that isn't a string when it exists at a StringTemplate.Processor call site would be terrible I think, php-grade terrible. At least as long as they don't also introduce that formatter-dot-template syntax which I'd consider a pointless redundancy with what kotlin already has: formatter{template} is already possible, expect for a way to write a template literal that resolves to something other than string.
What I certainly wouldn't expect is anything reminiscent of "automatically imported into every Java source file".
It's certainly a very visible language and one often talked about, but if you look at Google trends for Java versus kotlin you're looking at about 10% of the adoption that Java has or less.