16 ms·
JEP 430: String Templates (Preview) Proposed to Target Java 21
- lanna 4y agoAre they trying to make Scala look more complicated than the other languages on purpose? The Scala example f"$x%d plus $y%d equals ${x + y}%d" could be written simply as s"$x plus $y equals ${x + y}"
- geodel 4y agoYes, Java is legit scared of Scala's rising popularity.
- AtlasBarfed 4y agoIf anything Java is becoming groovy. Almost all features adopted are groovy features.
- oblio 4y agoIs Scala rising? It used to be hyped a ton back in the earlier Twitter days, about 10 years ago, but since then, everything has been quiet. If anything, Kotlin is probably ahead of Scala now, adoption wise.
- avgcorrection 4y agoWait. Did we just get teleported back to 2018?
- isbvhodnvemrwvn 4y agoSarcasm carries poorly over text :)
- ndriscoll 4y agoIt's funny too because the Scala ecosystem has had safe string interpolation for years. e.g. with Slick sql"SELECT * FROM Person p WHERE p.last_name = $lastName" will produce a parameterized query/prepared statement that you can run, and it does this at compile time so runtime injection like log4shell is impossible.
- optician_owl 4y agoOne of the best application I've seen is structured and contextual logging [1]. Combined with implicits, this allows you to require a right renderer or/and automatically get a current state of an execution context. 1. https://docs.tofu.tf/docs/tofu.logging.key-features/ https://docs.tofu.tf/docs/tofu.logging.key-features/
- MH15 4y agoA bit ugly, but seems powerful. Very similar to tagged template literals in JS.
- mabbo 4y agoString name = "Joan"; String info = STR."My name is \{name}"; I don't love it, to be honest. `STR` is a bit much. Requiring the \ for the first but not second brace. I want string templates in Java, but this feels ugly.
- lstamour 4y agoYeah, the choice of \{ to create an expression instead of printing a literal { character is an odd one when you compare how you output a double-quote character: > To aid refactoring, double-quote characters can be used inside embedded expressions without escaping them as \". I would expect for consistency that \"{expression}\" outputs an expression and \"\{expression}\" would not.
- kaba0 4y agoWhy would you have to escape ‘{‘? I don’t get your examples. The JEP meant that they can be used inside not, in-between, so: \{ “a” + “b” + 3 } could work.
- archgoon 4y ago[dead]
- v-erne 4y agoIm guessing that the second backslash was deemd unecessary (and in my opinion it is - no need for additional key strokes in the name of some arbitral consistancy). Besides you are probably looking at this wrong - backslash here is equivalent of hash or dolar sign - it denotes begining of expression (just like most languages do). And they literally could not use another one because of backward compatibilty it they want to keep the "no STR"/simple versuon of interpolation possible. They will just add new escape sequence /{ - its kinda brilliant if you think about it.
- pron 4y agoSTR is not some special syntax, just a receiver for a method call. You would only need STR to interpolate a template into a String, but any receiver can specify that interpretation of the template -- or a different one. So, for example, a logger could use: log.info."x: \{x}"; and similarly for other uses that don't produce strings but JSON, SQL etc. So STR would only be used -- hopefully rarely -- for old APIs that have not determined their own template interpretation strategy and only accept String. The use of the backslash is important to distinguish between a string literal and a template literal because "x: \{x}" is not a valid string literal today. Swift uses \(...), BTW.
- jerf 4y agoI am legit impressed, and I say this as one who has been very hard on string interpolation in other languages; see https://www.jerf.org/iri/post/2942/ https://www.jerf.org/iri/post/2942/ and the matching https://pkg.go.dev/github.com/thejerf/strinterp#section-readme https://pkg.go.dev/github.com/thejerf/strinterp#section-read... for instance. I have criticisms most developers don't even think about. This isn't exactly what I laid out, of course, but I think it achieves the goals I was looking for, which is the real issue. In particular, having the default string interpolation be prefixed with "STR." is enough for linters and scanners to get a chew on (it's easy by scanner standards to track that a STR.-interpolated string got fed into a database query), and for code reviewers to develop an instinct to look at such interpolations more closely than they need to for a DB. interpolation. An STR annotation is not technically necessary for the former, if it were simply the default, but it is a big deal for the latter. I want people to have a chance to notice and think about their use of bare string interpolation for at least a fraction of a second as they type "STR." (or autocomplete it or whatever). This does put a heavy burden on the libraries to implement it correctly, but if they do it's even safer than what I was thinking. One thing though: Please for the love of the internet, for those of you writing interpolators, DO NOT write an interpolator that picks apart the values passed in through a \{ ... } and starts instantiating arbitrary classes via Java reflection. Just stay away from that entirely, OK?
- jsiepkes 4y agoI guess it's an advantage of playing catch-up. You can learn from other languages their experience and mistakes. Still props for the Java team for doing a good job.
- pron 4y agoBack in 1997, when James Gosling outlined his vision for Java, he said it should be a conservative language (wrapping a very innovative runtime) that would ideally only adopt features that have proven themselves, for some time, in other languages. Being a last mover is at the very core of Java's evolution strategy. It's not playing catch-up because we're not trying to adopt all features other, more "adventurous", languages have, but rather to selectively pick the fewest features that would have the biggest impact. That's the aspiration, at least.
- Phelinofist 4y agoWhat happens if I already have a field called STR or FMT? > STR is a public static final field that is automatically imported in every Java source file.
- archgoon 4y ago[dead]
- kaba0 4y agoThen your STR would be used, and if you want to use this string template STR you would have to use it’s qualified name.
- ivan_gammel 4y agoOr have an alias: public static final ValidationProcessor $ = ….STR; $.“My name is \{name}“
- supriyo-biswas 4y agoThe proposal is uglier than f-strings in Python. A better approach would be to use backticks like JavaScript does.
- geodel 4y agoAs long it is technically solid, ugly is just fine in places where Java is used most. Even if it were most lovely syntax Python/JS devs are not gonna jump to code in Java.
- nezirus 4y agoThat database stuff looks horrible. Why do they feel the need to introduce DB query templating into string templates? No matter what you do on the client side, the database engine itself should escape/validate the data. Didn't we learn that lesson with PHP? In addition to that, not every database needs prepared statements for safe queries e.g. "Parametrized queries" in PostgreSQL (available in libpq as PQExecParams and exposed in many other higher level languages)
- bcrosby95 4y agoThe database stuff looks like an example, and shows how you could extend the template system in a way that doesn't introduce security problems.
- nezirus 4y agoThat is exactly the point, you should not use general string templating system for SQL queries, together with "roll your own" escape and validation mechanisms. I really don't see why they included that part, if not to show how to shoot yourself in the foot.
- bcrosby95 4y agoThere is no roll your own escape mechanics. The example uses prepared statements.
- imtringued 4y agoI am very confused by your comments. PHP developers thought "sanitizing" strings aka escaping and validating strings is enough to get rid of SQL injections and that is how they ended up with multiple iterations of escaping functions. The problem, which is the separation of code from data, has not been solved and that is why it is a bad idea. The SQL example template in the article uses positional parameters via JDBC and is therefore completely safe to use. It is impossible to get it wrong except by using STR which is obviously the wrong template processor.
- hibbelig 4y ago
- slt2021 4y agoFantastic and long awaited feature. Little bit clunky/ugly choice of chars, but I guess we will get used to it
- rcoveson 4y ago> Gosling: For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up.[0] So we can have this super powerful, extensible string interpolation system using never-before-seen syntax as a language feature, not a standard library extension, but not unsigned integers? I'll echo what other commenters have said: This looks powerful but ugly. I'll add to it by pointing out that "powerful but ugly" is what we've already got. So we gain the ability to move format arguments inline with the string contents if we're willing to use the `STR."\{}"` syntax? That seems like a lateral move. 0. http://www.gotw.ca/publications/c_family_interview.htm http://www.gotw.ca/publications/c_family_interview.htm
- pron 4y agoWe could have unsigned integers but we're choosing not to because on the whole their disadvantages outweigh their advantages. On the other hand, once we have user-defined value types, you'll be able to define unsigned integers in a library if you want. The extensibility and power of string templates were a requirement; security experts quite simply vetoed adding string interpolation as it's just too dangerous, especially in server-side software, where Java is mostly used. As to `STR.` -- which you won't need to use most of the time -- see my other comment: https://news.ycombinator.com/item?id=35013470 https://news.ycombinator.com/item?id=35013470
- Kwpolska 4y agoPython is also widely used server-side, and they introduced f-strings with simple and friendly syntax a few years ago. JS added template literals in 2015’s ES6, when Node.js was very much a thing. Why is Java special here?
- Noe2097 4y agoWhy oh why is that a backslash!? Over the 9 languages mentioned: 5 languages (inc. JVM ones like groovy and kotlin) are using `$`, 1 language (swift) is using `\`. `\` is a pain to type on many keyboard layouts -- actually most but the US one. It seemed to me that `$` would have been a much more "conventional" choice. This really makes me sad. It looks like the choice was made on purpose to be different.
- jacobn 4y agoI'd also really have preferred $, but apparently there's some backwards compatibility issue with using that character?
- kaba0 4y ago“${val}” is a legal string literal that is certainly used in plenty of existing programs. In current Java you would have to escape \, so only “\\{val}” could have existed before.
- _old_dude_ 4y agoThat's why JS use ` and not reuse ". You want the lexer to be able to make the difference between a constant string and a template string and you want users to be able to read the code. This JEP solves the former not the later.
- kaba0 4y agoIt’s not as bad as people make it out to be. Swift without any backwards compatibility constraints chose “\(val)”. It does need a slight getting used to but it is not any worse than ${}.
- Kwpolska 4y agoSwift’s choice is strange and ugly IMO, even if it did not involve backwards compatibility constraints.
- clhodapp 4y agoIt's interesting that this feature is essentially a clone of how string interpolation has been made extensible in Scala. However, the JEP only mentions Scala once.... in the form of an example which has been made strangely more complex than it has to be (it would be much shorter if they had used the s interpolator instead of the f interpolator). The one major difference is that, because Scala has macros and typeclasses, it can turn malformed interpolations for things like JSON or SQL into compile-time errors instead of runtime errors.
- _old_dude_ 4y agoIt seems the design team has lost its mojo. First, the lesson of Log4Shell have not been learnt. Allowing more libraries to do formatting, what can go wrong ? And String.format() is slow because every values are boxed. This JEP repeats the same mistake with the templates.
- pron 4y agoThe template instantiation itself is done by the language, not libraries, and the entire mechanism was designed for security (read the JEP); for example, templates are (virtually) limited to literals and can't come from user input. As to boxing, the built-in template processors, STR and FMT, don't do boxing (they use MethodHandle mechanisms similar to those used by lambdas) -- FMT is ~40x faster than String.format, I'm told -- but that capability hasn't been exposed as an API yet. As with other features, we try exposing basic usages first, and more sophisticated ones later.
- _old_dude_ 4y ago> the entire mechanism was designed for security ... I don't have the same definition of security (i've read the JEP). Unlike TypeScript, you can not type the template values. > built-in template processors, STR and FMT, don't do boxing ... but all the others user-defined template processors (think a logger) are second class citizens and will do boxing. Efficient String interpolation is hard without macros, that's why it's a macro in Rust. This JEP tries to solve a harder problem, user defined template processors but do not provide the tool to make them efficient and never will. Are you suggesting that there is a JEP about macros in the making ?
- pron 4y ago> Efficient String interpolation is hard without macros Not quite. It is hard without some compile-time computation, but what's compile-time for languages relying on AOT compilation (and requires macros or compile-time introspection as in Zig), happens at runtime in Java because that's when the optimising compilation happens, and Java has a user-exposed mechanism for defining call-sites (https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/CallSite.html https://docs.oracle.com/en/java/javase/19/docs/api/java.base...). It's just that, as with most of our new features, we expose a simple API first and a sophisticated API later. No need for macros.
- MBCook 4y agoI thought the choice of STR was ugly at first but as you continue reading and see how that allows multiple options that provide more than generic string joining it really starts to make sense. Unlike many here I do like the choice of \{}, although personally I would have preferred \() like Swift. I can’t wait.
- GoToRO 4y agoI wonder if lowercase "str." will work. Should be easier to type. I didn't find anything about that.
- MBCook 4y agoSTR seems to function like a class name, where you’re calling a static method. In fact that’s literally what you’re doing, but it seems to be syntactic sugar that you don’t have to write the method name and the parentheses. Since class names start with a capital it fits. At least I think that was the reasoning they stated. Why not Str? Perhaps they thought this would be less likely in existing code. I’m not sure I don’t think they mentioned the reason. They could have also just put it on the String class. Again I didn’t see a discussion as to why that wasn’t suggested. String.”Hello, \{name}.” Seems like it would have been reasonable. But this is a proposal so it may change before officially joining the JDK.
- zaphirplane 4y ago+1 The syntactic sugar should be a feature then used used here rather than invent a special cased syntax for a feature just use a conventional calling syntax. Java is a fairly verbose language with a highly verbose popular patterns
- kaba0 4y agoSTR is a static field, and the syntax sugar is equivalent of StringTemplate t = RAW.”Hello, \{name}.” STR.process(t);
- kaba0 4y ago
- ivan_gammel 4y agoI love it. The design choices are great, given the number of constraints. Looking forward to IDE and Maven plugin support of this feature, which can add a lot of juice in pre-compile time enabling very well integrated DSLs.
- tofflos 4y agoI wonder if annotations and annotation pre-processors could have been an alternate way to approach this had they been applicable to String constants. String name = "Joan"; PreparedStatement query = @SQL "select * from users where firstname = \{name}";
- ivan_gammel 4y agoAnnotations are declarative, processors are imperative. You need an interface with a method to run the processing.
- thamer 4y agoThis also made me think of prepared statements. It would be more readable to have all the values in their respective place in the SQL string, rather than have a bunch of question marks followed by all the values bunched together at the end. e.g. this: PreparedStatement query = SQL."SELECT * FROM users WHERE firstname=\{firstName} and lastname=\{lastName} and email=\{email}"; rather than: query = client.prepare("SELECT * FROM users WHERE firstname=? and lastname=? and email=?", firstName, lastName, email);
- kaba0 4y agoThis literally even has examples in the JEP, one can create custom template processors which don’t even have to return a String, but can return a PreparedStatement instead.
- spankalee 4y agoThis looks great. I maintain the lit-html template library which uses JavaScript tagged template literals to embed HTML in JS. A key feature of JS template literals is that the tag receives the string fragments and values separately, and can return non-string results. This means we can escape values and validate template structure to prevent XSS attacks. This approach in Java should lead to a lot more lightweight but safe DSL embeddings. (I don't love the syntax, but... it's Java) edit: the other critical feature of JS tagged template literals, that I'm not sure this has, is referential equality of the template strings passed to the tag function across multiple invocations. This is required to be able to do one-time preparation work on a template and re-use that with different sets of values. Think a `SQL.` template processor that turns the strings into a prepared statement once and re-uses that for each query with different parameters. edit 2: awesome, it does: The fragments() of a StringTemplate are constant across all evaluations of a template expression, while values() is computed fresh for each evaluation. For example:
- aardvark179 4y agoThis spec doesn’t need to specify that as it should fall out naturally from Constable and co (JEP 303 and 334) in combination with string templates.
- ko27 4y agoIt's interesting how C# is always far ahead of Java, they introduced it way earlier, the syntax is simpler, and you can make is safe by using FormattableString as the param type, for example in EF you can do this without worrying about SQL injection: FromSql($"EXECUTE dbo.GetMostPopularBlogsForUser {user}") https://learn.microsoft.com/en-us/ef/core/querying/sql-queries#passing-parameters https://learn.microsoft.com/en-us/ef/core/querying/sql-queri...
- deleted 4y ago[deleted]
- DaiPlusPlus 4y agoA major shortcoming with FormattableString is that the C# compiler is hard-coded to always prefer implicitly converting an interpolated string to a string, which makes it impossible to write extension-methods for FormattsbleString objects. …and they also always default to formatting with CurrentCulture instead of InvarintCulture. Apparently this was by-design as interpolated strings were never originally intended for generating machine-readable strings. Finally, there’s no way to perform common “mini-templating” with a FormattableString, such as repeating regions, show/hide regions within a string, or little things like inflection (e.g. rendering “{0:N} items” or “{0:N0} item” when arg0 is 1 or not. I’m happy to see Java (finally) gain a similar feature, but it, like C#,s, seems… limited in its abilities.
- brewmarche 4y agoC# 10 introduced interpolated string handlers. They allow to address some of your points (you can handle format strings however you want and you could also choose invariant culture by default, without using FormattableString.InvariantCulture) and at the same time avoid allocations.
- pjmlp 4y agoNot always, e.g. default interface methods, just to give one example.
- knightofmars 4y ago
- deleted 4y ago[deleted]
- kaba0 4y agoNot sure where should I give this minor heads up regarding the JEP: StringProcessor INTER = (StringTemplate st) -> { String placeHolder = "•"; String stencil = String.join(placeHolder, fragments); for (Object value : st.values()) { String v = String.valueOf(value); stencil = stencil.replaceFirst(placeHolder, v); } return stencil; }; ‘fragments’ should be ‘st.fragments()’ here I believe to make it compile.
- ninkendo 4y agoOne thing I'm not sure is possible in this proposal, is there a way to make the interpolation "lazy", such that the string (and the evaluation of its interpolated components) can be skipped if the string isn't ultimately used? In swift, there's some nice quasi-laziness you can add to function parameters, so that (say) a logging function that can fully skip evaluating a string sent to it with the `@autoclosure` syntax: func log(message: @autoclosure () -> String, level: Level = .info) { guard level >= configuredLevel else { return } actuallyLog(message()) } And call it like: log(message: "User is \(someExpensiveFunction(user))", level: .debug) And if the configured log level does not include debug messages, `someExpensiveFunction(user)` doesn't get called. This works because @autoclosure lets you take a parameter that is "function returning String", but callers can just pretend they're passing it a String, without having to decorate it in a function. The compiled code will turn it into a closure behind the scenes, and thus it'll be evaluated lazily. Not sure if there's any way to do something like this in Java with this proposal...
- lazulicurio 4y agoIt's a tad more verbose, but I assume the simplest way would just be to use a standard closure. void log(Supplier<? extends String> messageSupplier); log(() -> log.info"User is \{someExpensiveFunction(user)}"); The pattern of using Supplier to provide a lazy argument is pretty well-established afaict.
- layer8 4y agoYou probably would pass the equivalent of new Object() { public String toString() { return <actual expression here> ; } } You can wrap that into a helper function that takes a lambda, so you can write STR."\{lazy(() -> <actual expression here>)}"
- ufo 4y agoDoes any one know what are the advantages of STR."templ" syntax vs STR("templ")?
- kaba0 4y ago“templ” would be an ordinary string literal in the second case, in the former case the parser can already distinguish between the two.
- cbeach 4y agoJava: String info = STR."My name is \{name}"; Scala: val info = s"My name is $name" Why does Java ALWAYS use unnecessary verbosity? I don’t get it. They’ve had years to learn from succinct languages.
- crummy 4y agoBackwards compatibility?
- rossjudson 4y agoI think you mean Java: var info = STR."My price is $\{price}_"; Scala: val info = s"My price is $$${price}_"
- jillesvangurp 4y agoKotlin: val info = "My name is $name" All strings are templatable in Kotlin. No need to have different types of syntax for that. What purpose does this s or STR. prefix serve serve? Seems completely redundant to me to have that.
- MrBuddyCasino 4y agoIt helps to actually read the article. There will be multiple template processors, not just one. This proposal goes beyond simple string interpolation and adds security measures against injection attacks. I'm mostly developing in Kotlin btw., but usually when Java introduces its take on a feature that has existed for a while in other languages, they improve on it. One such example: handling whitespace/indentation in multi-line strings in Kotlin vs Java. In Kotlin you usually have to call .trimIndent(), in Java the defaults are sane so you don't have to.
- jillesvangurp 4y agoThe article in this case is a longish specification. I skimmed through it. It looks over engineered to me. That's my honest first impression. A dead give away for that is that it needs a longish specification. And I indeed missed the bit about a pluggable template engine. Just one question related to that: why? I don't see the big use case for this. Maybe a bit less flexibility and a more sane syntax would be better?
- deleted 4y ago[deleted]
- atbpaca 4y agoplease, just align with other languages and use "$" instead of "\{".
- tastyfreeze 4y agoThis is one feature I have wanted in Java for a long time. String::format is close but feels clunky. Anonther feature I would like to see added is operator overloading so we can do away with ugly method calls for BigDecimal arithemetic.
- singularity2001 4y agoyuck, java never fails to make the syntax awkward