Type parameters and other topics regarding String Templates
Johannes Spangenberg
johannes.spangenberg at hotmail.de
Sun Oct 29 10:56:58 UTC 2023
> Hello!
Thanks for the reply!
> *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.
>
I do not see the problem with spelling out the concrete processor type.
Based on my experience, I expect it to be better for readability to
create a separate class anyway. However, I agree that it is non-ideal
that you have to do so in an exported package. Especially considering
that it is not trivial to restrict access to the constructor when the
field and the type of the processor are in different packages. Maybe you
are right.
> 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?
I am reluctant about this. I would probably continue to implement the
generic interface just because it is more specific. I would like to
avoid a scenario where I did not implement a more specific interface out
of ignorance, and some other library is now expecting that interface. I
guess it is better not to open up this room for inconsistency between
processors.
>
> 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.
You may be right.
> 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?
I am aware that this limitation will prevent most processors from using
a more specific type. I am mostly just reluctant to give up type-safety.
You might be able to use typed parameters in some more advanced use
cases. I could imagine to define a Swing Layout using a template string
and a DSL. The DSL might be somewhat similar to grid-template in CSS.
All the template arguments would need to be Swing components.
Container contentPane = ...;
contentPane.setLayout(LAYOUT."""
\{logo} \{toolbar} \{userinfo} fit-content
\{navigation} \{content} \{content} minmax(100px, auto)
\{footer} \{footer} \{footer} fit-content
100px minmax(100px, auto) fit-content
""");
I would also be in favor of getting union types. I was often struggling
with this limitation independent from String Templates. Anyway, unions
are different topic. (If you are aware of any active discussions or
previous attempts, I would be happy to know. I would also have an idea
how to represent them in Bytecode, but not sure how well the problem
itself was discussed yet.)
> *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)}
> """
Good point. I would probably still prefer to implement my own template
processor, but String.indent(int) already makes it less annoying if you
don't have one. It might still be good to have an option or flag for
FMT, but that would be out of scope of this JEP.
> 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/484da577/attachment-0001.htm>
More information about the amber-dev
mailing list