Hello,
> Another, simpler, option we consider was to use some kind of prefix to mark a
> string template literal (e.g. make that explicit, instead of resorting to
> language wizardry). That works, but has the disadvantage of breaking the
> spell that there is only “one string literal”, which is something we have
> worked quite hard to achieve.
I vote for making string templates explicit.
Yes, it can be seen as complex as first because not everything is a String, but
at the same time, I believe it makes the conversion rules far easier to
understand.
For me, we already have a lot of methods that takes a String as parameter, so
seeing String as a StringTemplate is not really a solution because it means
adding a lot of overloads with all of the incompatibilities you describe.
I see the conversion of a StringTemplate to a String as a boxing conversion, if
the current type is a StringTemplate and the target type is a String, the
compiler will generate a code that is equivalent to calling ".interpolate()"
implicitly.
It's not a real boxing conversion, because it's a one way conversion, i.e.
there is a boxing conversion between StringTemplate to String but no boxing
conversion from String to StringTemplate. We can add it, but i do not think
it's necessary given that with a String s, it can always be converted to a
StringTemplate using t"\{s}".
One question can be if we prefer a callsite conversion like the boxing
conversion described above or a declaration site conversion, i.e. ask all
developers if they want to support StringTemplate to add a new overload.
Apart from the fact that adding overloads in a lot of existing projects looks
like a sisiphus task, doing the conversion at use site also as the advantage of
allowing the compiler generates an invokedynamic at use site so the boxing from
a StringTemplate to a String will be as fast as the string concatenation using
'+' (see Duncan email on amber-dev).
regards,
Rémi
> From: "Maurizio Cimadamore" <[email protected]>
> To: "Brian Goetz" <[email protected]>, "Guy Steele"
> <[email protected]>
> Cc: "Tagir Valeev" <[email protected]>, "amber-spec-experts"
> <[email protected]>
> Sent: Monday, March 11, 2024 1:15:51 PM
> Subject: Re: Update on String Templates (JEP 459)
> Hi all,
> we tried mainly three approaches to allow smoother interop between strings and
> string templates: (a) make String a subclass of StringTemplate. Or (b) make
> constant strings bs convertible to string templates. Or, (c) use
> target-typing.
> All these approaches have some issues, discussed below.
> The first approach is slightly simpler, because it can be achieved entirely
> outside of the Java language. Unfortunately, adding “String implements
> StringTemplate” adds overload ambiguities in cases such as this:
> format(StringTemplate) // 1
> format(String, Object...) // 2
> This is actually a very important case, as we predice that StringTemplate will
> serve as a great replacement for methods out there accepting a string/Object…
> pack.
> Unfortunatly, if String <: StringTemplate, this means that calling format
> with a
> string literal will resolve to (1), not (2) as before. The problem here is
> that
> (2) is not even applicable during the two overload resolution phases (which is
> only allowed to use subtyping and conversions, respectively), as it is a
> varargs method. Because of this, (1) will now take the precedence, as that’s
> not varargs. While for String::format this is probably harmless, changing
> results of overload selection is something that should be done with care (esp.
> if different overloads have different return types), as it could lead to
> source
> compatibility issues.
> On top of these issues, making all strings be string templates has the
> disadvantage of also considering “messy” strings obtained via concatenation of
> non-constant values string templates too, which seems bad.
> To overcome these issues, we attempetd to add an implicit conversion from
> constant strings to StringTemplate. As it was observed by Guy, in case of
> ambiguities, the non-converting variants (e.g. m(String)) would be preferred.
> That said, in the above example (with varargs) we would still get a
> potentially
> incompatible change - as a string literal would be applicable in (1) before
> (2)
> is even considered, so the same concerns surrounding overload resolution
> changes would remain.
> Another thing that came up is that conversions automatically bring in casting
> conversions. E.g. if you can go from A to B using assignment conversion, you
> can typically go the same direction using casting conversion. This raises two
> issues. The first is that casting conversion is generally a symmetric type
> relationship (e.g. if you can cast from A to B, then you can cast from B to
> A),
> while here we’re mostly discussing about one direction. But this is, perhaps,
> not a big deal - after all, “constant strings” don’t have a denotable type, so
> perhaps it should come to no surprise that you can’t use them as a target type
> for a cast.
> The second “issue” is that casting conversion brings about patterns, as that’s
> how pattern applicability is defined. For instance:
> switch("Hello") {
> case StringTemplate st ...
> }
> To make this work we would need at least to tweak exhaustiveness (otherwise
> javac would think the above switch is not exhaustive, and ask you to add a
> default). Secondly, some tweaks to the runtime tests would be required also.
> Not impossible, but would require some more work to make sure we’re ok with
> this direction.
> Another issue with the conversion is that it would expose a sharp edge in the
> current overload resolution and inference machinery. For instance, this
> program
> doesn’t compile correctly:
> List<Integer> li = List.of(1, 1L)
> Similarly, this program would also not compile correctly:
> List<StringTemplate> li = List.of("Hello", "Hello \{world}");
> The last possibility would be to say that a string literal is a poly
> expression
> . As such, a string literal can be typed to either String or StringTemplate
> depending on the target type (for instance, this is close to how int literals
> also work).
> This approach would still suffer from the same incompatible overload changes
> with varargs method as the other approaches. But, by avoiding to add a
> conversion, it makes things a little easier: for instance, in the case of
> pattern matching, nothing needs to be done, as the string literal will be
> turned into a string template before the switch even takes place (meaning that
> existing exhaustiveness and runtime checks would still work). But, there’s
> still dragons and irregularities when it comes to inference - for instance:
> List<StringTemplate> lst = List.of("hello", "world");
> This would not type-check: we need a target-type to know which way the literal
> is going (List::of just accepts a type-variable X). Note that overload
> resolution happens at a time where the target-type is not known, so here we’d
> probably pick X = String, which will then fail to type-check against the
> target.
> Another issue with target-typing is that if you have two overloads:
> m(String)
> m(StringTemplate)
> And you call this with a string literal, you get an ambiguity: you can go both
> ways, but String and StringTemplate are unrelated types, so we can’t pick one
> as “most specific”. This issue could be addressed, in principle, by adding an
> ad-hoc most specific rule that, in case of an ambiguity, always gave
> precedence
> to String over StringTemplate. We do a similar trick for lambda expressions,
> where if two method accepts similarly looking functional interface, we give
> precedence to the non-boxing one.
> Anyway, the general message here is that it’s a bit of a “pick your posion”
> situation. Adding a more fluid relationship between string and templates is
> definitively possible, but there are risks that this will impact negatively
> other areas of the language, risks that would need to be assessed very
> carefully.
> Another, simpler, option we consider was to use some kind of prefix to mark a
> string template literal (e.g. make that explicit, instead of resorting to
> language wizardry). That works, but has the disadvantage of breaking the spell
> that there is only “one string literal”, which is something we have worked
> quite hard to achieve.
> Cheers
> Maurizio
> On 09/03/2024 23:52, Brian Goetz wrote:
>> I’ll let Maurizio give the details, because I’m sure I will have forgotten
>> one
>> or two.
>