<div dir="ltr">If the string processing burden is now pushed to the consumer API side, then wouldn't it be worthwhile to make `StringTemplate` simpler given that this means a lot more people are forced to implement processors? I mean that having two lists where you have to alternate between the two is rather unintuitive which is proven by the fact that it forces `StringTemplate` to do the empty string hacks to support alternating between the two lists.<div><br></div><div>Given that we have these nice pattern matching syntaxes, wouldn't it be much nicer to make `StringTemplate` to be a simple wrapper for a `List<StringTemplate.Part>`, where `StringTemplate.Part` is a sealed interface implemented by `String` and `StringTemplate.ValueRef` (or whatever equivalent). In this case, you could just write a processor with a simple loop like this:</div><div><br></div><div>```</div><div>var sb = new StringBuilder();</div><div>st.parts().forEach(part -> {</div><div>  switch (part) {</div><div>    case String -> sb.append(part);</div><div>    case StringTemplate.ValueRef -> sb.append(formatValue(valueRef.value()));</div><div>  }</div><div>})</div><div>```</div><div><br></div><div>A processor logic would be just much more easier to read than the double iterator counterpart (and in my opinion even easier than trying to use the stencil). An added benefit is that there would be little need to ban a character from ST in this case. Of course, the flip side is that we would need all values to be wrapped, but that doesn't seem like a high cost to me (especially if `ValueRef` would eventually be a value type, then I'm guessing this extra cost would be possible to be mostly optimized away), because it is unlikely to have so many values in an ST for this to matter. Not to mention that having double iterators would have additional cost as well.</div><div><br></div><div>Attila</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> ezt írta (időpont: 2024. márc. 8., P, 21:54):<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">



<div style="overflow-wrap: break-word;">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Time to check in with where were are with String Templates.  We’ve gone through two rounds of preview, and have received some feedback.  </span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000">As a reminder, the primary goal of gathering feedback is to learn things about the design or implementation that we don’t already know.  This could be bug reports, experience reports, code review, careful analysis, novel alternatives,
 etc.    And the best feedback usually comes from using the feature “in anger” — trying to actually write code with it.  </font><span style="color:rgb(0,0,0)">(“Some people would prefer a different syntax” or “some people
 would prefer we focused on string interpolation only” fall squarely in the “things we already knew” camp.) </span>
<div><font color="#000000"><span><br>
</span></font>
<div>
<div><font color="#000000">In the course of using this feature in the `jextract` project, we did learn quite a few things we didn’t already know, and this was conclusive enough that it has motivated us to adjust our approach in this feature.
  Specifically, the role of processors is “outsized” to the value they offer, and, after further exploration, we now believe it is possible to achieve the goals of the feature without an explicit “processor” abstraction at all!  This is a very positive development.
  </font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">First, I want to affirm that that the goals of the project have not changed.  From JEP 459:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Goals</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Simplify the writing of Java programs by making it easy to express strings
 that include values computed at run time.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Enhance the readability of expressions that mix text and expressions, whether
 the text fits on a single source line (as with string literals) or spans several source lines (as with text blocks).</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Improve the security of Java programs that compose strings from user-provided
 values and pass them to other systems (e.g., building queries for databases) by supporting validation and transformation of both the template and the values of its embedded expressions.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Retain flexibility by allowing Java libraries to define the formatting syntax
 used in string templates.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Simplify the use of APIs that accept strings written in non-Java languages
 (e.g., SQL, XML, and JSON).</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• Enable the creation of non-string values computed from literal text and embedded
 expressions without having to transit through an intermediate string representation.</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Non-Goals</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• It is not a goal to introduce syntactic sugar for Java's string concatenation
 operator (+), since that would circumvent the goal of validation.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0);white-space:pre-wrap"></span><span style="color:rgb(0,0,0)">• It is not a goal to deprecate or remove the StringBuilder and StringBuffer
 classes, which have traditionally been used for complex or programmatic string composition.</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Another thing that has not changed is our view on the syntax for embedding expressions.  While many people did express the opinion of “why not ‘just' do what Kotlin/Scala does”, this issue
 was more than fully explored during the initial design round.  (In fact, while syntax disagreements are often purely subjective, this one was far more clear — the $-syntax is objectively worse, and would be doubly so if injected into an existing language where
 there were already string literals in the wild.  This has all been more than adequately covered elsewhere, so I won’t rehash it here.)</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Now, let’s talk about what we do think should change: the role of processors and the StringTemplate type.  </span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000">Processors were envisioned as a means to abstract the transformation of templates to their final form (whether string, or something else.)  However, Java already has a well established means of abstracting behavior: methods.  
 (In fact, a processor application can be viewed as merely a new syntax for a method call.)  Our experience using the feature highlighted the question: When converting a SQL query expressed as a template to the form required by the database (such as PreparedStatement),
 why do we need to say:</font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">  DB.”… template …”</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">When we could use an ordinary Java library:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">  Query q = Query.of(“…template…”)</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000">Indeed, one of the worst things about having processors in the language is that API designers are put in the difficult situation of not knowing whether to write a processor or an ordinary API, and often have to make that choice
 before the consequences are fully understood.  (To add to this, processors raise similar questions at the use site.) But the real criticism here is that template capture and processing are complected, when they should be separate, composable features.  </font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">This motivated us to revisit some of the reasons why processors were so central to the initial design in the first place.  And it turned out, this choice had been influenced — perhaps overly
 so — by early implementation experiments.  (One of the background design goals was to enable expensive operations like `String::format` to be (much) cheaper.  Without digressing too deeply on performance, String::format can be more than an order of magnitude
 worse than the equivalent concatenation operation, and this in turn sometimes motivates developers to use worse idioms for formatting.  The FMT processor brough that cost back in line with the equivalent concatenation.)  These early experiments biased the
 design towards needing to know the processor at the point of template capture, but upon reexamination we realized that there are other ways to achieve the desired performance goals without requiring processors to be known at capture time.  This, in turn, enabled
 us to revisit a point in the design space we had transited through earlier, where string templates were “just a new kind of literal” and the job performed by processors could instead be performed by ordinary APIs.</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">At this point, a simpler design and implementation emerged that met the semantic, correctness, and performance goals: template literals (“Hello \{name}”) are simply the literal form of StringTemplate:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000"><span>  StringTemplate st = “Hello \{name}”;</span></font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000"><span>String and StringTemplate remain unrelated types.  (We explored a number of ways to interconvert them, but they caused more trouble than they solved.)  Processing of string templates,
 including interpolation, is done by ordinary APIs that deal in StringTemplate, aided by some clever implementation tricks to ensure good performance.  </span></font></div>
<div><font color="#000000"><span><br>
</span></font></div>
<div><font color="#000000"><span>For APIs where interpolation is known to be safe in the domain, such as PrintWriter, APIs can make that choice on behalf of the domain, by providing overloads to
 embody this design choice: </span></font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">   void println(String) { … }</span><br style="color:rgb(0,0,0)">
<font color="#000000">   void println(StringTemplate) { … interpolate and delegate to println(String) …. }</font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">The upshot is that for interpolation-safe APIs like println, we can use a template directly without giving up any safety:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">   System.out.println(“Hello \{name}”);</span><br style="color:rgb(0,0,0)">
<br>
In this example, the string template evaluates to StringTemplate, not String (no implicit interpolation), and chooses the StringTemplate overload of println, which in turn chooses how to process the template.  <span style="color:rgb(0,0,0)">This
 stays true to the design principle that interpolation is dangerous enough that it should be an explicit choice in the code — but it allows that choice to be made by libraries when the library is comfortable doing so.  </span></div>
<div><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Similarly, the FMT processor is replaced by an overload of String::format that interprets templates with embedded format specifiers (e.g., “%d”):</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">  String format(String formatString, Object… parameters) { … same as today … }</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">  String format(StringTemplate template) {... equivalent of FMT ...}</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">And users can call this as:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">  String s = String.format(“Hello %12s\{name}”);</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000"><span>Here, the String::format API has chosen to interpret string templates according to the rules previously specified in the FMT processor (not ordinary interpolation), but that choice
 is embedded in the library semantics so no further explicit choice at the use site is required.  The user already chose to pass it to String::format; that’s all the processing selection that is needed.  </span></font><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">Where APIs do not express a choice of what template expansion means, users continue to be free to process them explicitly before passing them, using APIs that do (such as String::format
 or ordinary interpolation.). </span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">The result is:</span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- The need for use-site "goop" (previously, the processor name; now, static or instance methods to process a template) goes away entirely when dealing with libraries that are already template-friendly.
  </span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- Even with libraries that require use-site goop, it is no more intrusive than before, and can be reduced over time as APIs get with the program.  </span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- StringTemplate is just another type that APIs can support if they want.  The "DB" processor becomes an ordinary factory method that accepts a string template or an ordinary builder API.
  </span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- APIs now can have _more_ control over the timing and meaning of template processing, because we are not biasing so strongly towards early processing.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- It becomes easier to abstract over template processing (i.e., combine or manipulate templates as templates before processing)</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- Interpolation remains an explicit choice, but ST-aware libraries can make this choice on behalf of the user.</span><br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">- The language feature and API surface get considerably smaller, which is good.  Core JDK APIs (e.g., println, format, exception constructors) get upgraded to work with string templates.
  </span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<span style="color:rgb(0,0,0)">The remaining question that everyone is probably asking is: “so how do we do interpolation.”  The answer there is “ordinary library methods”.  This might be a static method (String.join(StringTemplate))
 or an instance method (template.join()), shed to be painted (but please, not right now.). </span><br style="color:rgb(0,0,0)">
<br style="color:rgb(0,0,0)">
<font color="#000000"><span>This is a sketch of direction, so feel free to pose questions/comments on the direction.  We’ll discuss the details as we go. </span></font></div>
</div>
</div>
<div><font color="#000000"><span><br>
</span></font></div>
<div><font color="#000000"><span><br>
</span></font></div>
</div>

</blockquote></div>