Update on String Templates (JEP 459)

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Mar 15 00:49:48 UTC 2024


On 15/03/2024 00:24, Robbe Pincket wrote:
>
> On 14/03/2024 23:44 UTC, Maurizio Cimadamore wrote:
>
>            On 14/03/2024 22:36, Robbe Pincket wrote:
>
> (I’m not a big fan of `TEMPLATE"Foo: \{bar}"` either as it’s just so 
> much longer than `"Foo: " + bar`)
>
>            Note that when I suggested TEMPLATE as a prefix I was 
> obviously not being super serious :-)
>
>            Let's do a test (bear with me). Let's assume the two 
> prefixes were S and T (not saying I like them, just trying them out 
> for size). Let's also assume there's no conversion. Then your examples 
> become:
>
>            ```
>
>            String s1 = "test" // still a string literal
>
> StringTemplate st2 = T"test" // allowed, constant strings can be 
> implicitly converted to templates
>
> StringTemplate st3 = "Foo: \{bar}" // Simple string template
>
>            String s4c = S"Foo: \{bar}" // short for String.of("Foo: 
> \{bar}")
>
>            ```
>
>            I think that's not too bad? (please don't focus too much on 
> the letters).
>
>            In the sense: the rare cases (st2) has a prefix. And the 
> operation we want explicit (s4c) also has a prefix. Everything else is 
> fine.
>
> So the difference is that `T` (or something else) has to be used for 
> templates without any holes?
>
> To me it feels a bit weird to have a prefix for the special case of a 
> hole-less template.
>
Ok. Note that T would be required in that case, but one might also use 
it as a visual delimiter: if a template is very long, it might not be 
too readable to leave it implicit as to whether the thing in quotes is a 
template or not.
>
> I think I saw an argument passing by, saying something along the line 
> that `String` and `StringTemplate` are semantically different so 
> implicit conversion in either direction would be bad because it would 
> be ambigous.
>
> If this `T` idea is based on that, I don't really see why it would be 
> that bad. If an API accepts either, I would intuitivly expect that 
> passing a string and passing a hole-less template with the same string 
> would give me the same result.
>
>            Control question #1: does the conversion here change things 
> much? Or, are we reaching for conversions just to have something 
> "shorter" ?
>
>            Control question #2: let's now assume that S and T were 
> spelled (String) and (StringTemplate), respectively. How do we feel 
> about this?
>
>            ```
>
>            String s1 = "test" // still a string literal
>
> StringTemplate st2 = (StringTemplate)"test" // allowed, cast from 
> constant string to template
>
> StringTemplate st3 = "Foo: \{bar}" // Simple string template
>
>            String s4c = (String)"Foo: \{bar}" // allowed, cast from 
> template back to String (interpolation)
>
>            ```
>
> I think I answered #1. Having the 'T' *just* for the "hole-less" 
> template feels a bit odd? I don't you can sell me on #2.
>
Fair enough - I had to ask :-)
>
> If ´(StringTemplate)"test"` and `(String)"Foo: \{bar}"` are valid, 
> will the following things work too? `"test" instanceof StringTemplate 
> template` and `"Foo: \{bar}" instanceof String str`. The second one 
> I'd assume no, the first one is a bit unclear to me.
>
These are questions I raised even in the context of the implicit 
conversion you are advocating for: once you add an assignment 
conversion, cast comes with it, and with cast, patterns and instanceof. 
In other words, that's the price we have to pay for eliminating the T in 
the hole-less template in the way you proposed. Casts just make that 
trade-off more explicit.

Another thing I don't love about implicit conversion, is that they don't 
play with inference too well:

```
List<StringTemplate> ls = List.of("Hello");
```

The above would be an error. The type-variable (X) for List::of is 
seeing two different constraints:

* X = StringTemplate (from the target type)
* String <: X (from the argument)

This fails, because we'd infer StringTemplate which is a supertype of 
String. Even if we could somehow "convince" inference that 
StringTemplate is a valid "more general" type, I see lots and lots of 
dragons here:

* inference would have to be careful only to do certain moves if 
constant strings are involved
* if we pick StringTemplate, we're basically saying that the method is 
applicable by conversion, so in overload step 2. But is this the 
overload step we used to pick that candidate in the first place? 
Probably not, because String <: X requires only subtyping, not conversion.

Ultimately, implicit conversion would only "kind of work" and will lead 
to issues when interacting with generics. While it's tempting to sweep 
issues under the rug (after all they do not seem very important for the 
examples we're discussing), such compromises have a tendency to bit us 
back when feature "grow up" and start playing more with other feature: 
any loss of compositionality there costs quite a bit. Which is why I'm 
not in love with implicit conversions

Maurizio


>            Maurizio
>
> Kind regards
>
> Robbe Pincket
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20240315/a2e3095c/attachment-0001.htm>


More information about the amber-spec-observers mailing list