Type parameters and other topics regarding String Templates

Tagir Valeev amaembo at gmail.com
Sun Oct 29 07:14:18 UTC 2023


Hello!

On Sun, Oct 29, 2023 at 2:16 AM Johannes Spangenberg <
johannes.spangenberg at hotmail.de> wrote:

> Hello, I recently thought about String Templates and just read the new JEP
> 459 for the second preview. I have four comments about the current JEP,
> more or less ordered by importance.
>
> *1) Unusual generic types for exceptions and return values*
>

The convenience of generic types is that you don't always need to spell out
or even publicly declare the concrete processor type. However, I agree that
generic types is not what strictly needed to be mentioned in the
specification. We can have

// This type is mentioned in the specification as the root type of
string template
// clients may implement it directly using more specific return type and
exception declaration
interface Processor {
  Object process(StringTemplate template) throw Throwable;
}

// This is just a convenient generic subtype, not mentioned in the
specification
// for people who don't want to publicly declare their own types for
processors
@FunctionalInterface
interface ParameterizedProcessor<R, E extends Throwable> extends  Processor
{
  R process(StringTemplate template) throws E;

  static <R, E extends Throwable> ParameterizedProcessor<R, E>
of(Function<? super StringTemplate, ? extends T> process) {
    return process::apply;
  }
}

The problem is more with the naming: how to name these two things to
clearly show the difference between them?


> The generic types as specified by JEP 459 look like remnants to me. Since
> template expressions are syntactic sugar for method calls, we should just
> use the type of the process method, as was introduced by this JEP. However,
> the generic types were somehow left in the JEP. Is there a good reason for
> that?
>
> For example, java.lang.AutoCloseable does not have a generic type for the
> exception. The only advantage I see is that you can implement processors
> using Lambdas without introducing a functional interface. How often do you
> have a processor that is trivial enough that you see the benefit of using a
> lambda? Even the implementation of STR takes multiple lines. If somebody
> needs a functional interface, it is trivial to create one yourself.
>
> In my opinion, the noise introduced by the template parameters greatly
> overshadows the advantage of lambdas without having to declare a functional
> interface first.
>

On the other hand, you may not have such noise even now, if you declare
your own concrete types that inherit the generic processor.


> For RAW, STR, and FMT, you may either introduce a sub-interface without
> the generic exception or just implement them within their own class outside
> java.lang.
>
> *2) Generic type for embedded expressions*
>
> While I don't see the value of the existing type parameters, I think it
> would be helpful to have a generic type for the embedded expressions. In
> contrast to the existing type parameters, such a type parameter would
> extend the capabilities. It would be possible to have type-safe inputs for
> processors, which is not possible with the current JEP.
>
> public interface StringTemplate<T>
>     ...
>     public interface Processor<T> {
>         Object process(StringTemplate<? extends T> st) throws Throwable;
>     }
>     ...
> }
>
>
This covers only a limited scenario when all the embedded expressions have
the same type. Can you imagine a particular use-case for that scenario?

>
> *3) Let STR and FMT keep the indentation *
>
> I was wondering if STR and FMT should keep the intention when the
> arguments contain line breaks. Most usages are probably not indented or
> only use arguments without line breaks. However, whenever I encountered a
> situation where I wanted to insert a multiline string into an indented
> line, I always wanted to keep the intention. A potential implementation
> could basically copy all the whitespace characters directly after the most
> recent line break in the fragments and insert them behind each line break
> of the template argument.
>
> String multilineString = """
>     First line.
>     Second line.
>     """;
> System.out.println("""
>     Output:
>
>         \{multilineString}
>
>     ...
>     """);
>
> I have to say I am myself a bit skeptical as this would noticeably
> increase the complexity. However, I cannot think of a scenario where you
> don't want this to happen. (I could imagine that I would otherwise
> implement such a processor in almost all my projects and end up never using
> STR.)
>
You can use instead
STR."""
Output:
\{multilineString.indent(4)}
"""
Which would be more explicit, thus less magical. In general, it's expected
that a properly written program would only rarely use STR processor
(especially for multiline output), preferring more domain-specific
processors depending on how the resulting string should be interpreted.


> *4) Purpose of StringTemplate.interpolate()?*
>
> And finally, although not very important, I am wondering about the
> usefulness of StringTemplate.interpolate. In the rare case when you
> actually need this functionality, you can also call STR.process(template).
> There is also template.process(STR) for some reason.
> For toString()-like use cases, we already have
> StringTemplate.toString(StringTemplate). I don't think that
> StringTemplate.interpolate() would be a good fit in such cases. And
> again, you can otherwise still call STR.process(...).
>
>
> That was all. I am happy for any reply.
>
> PS: I find it interesting that the JEP does not mention tagged templates
> of JavaScript, although the concepts look quite similar.
>
> Best Regards,
> Johannes
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20231029/7cadc352/attachment-0001.htm>


More information about the amber-dev mailing list