Hello, Maurizio!
Thank you for the detailed explanation!
On Mon, Mar 11, 2024 at 1:16 PM Maurizio Cimadamore
<[email protected]> wrote:
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.
I would still like to advocate for String <: StringTemplate
solution. I think that the overloading is not a big problem.
Simply making String implements StringTemplate will not break any
of existing code because there are no APIs yet that accept the
StringTemplate instance. The problem may appear only when an API
author actually adds such an overload and does this in an
incompatible way with an existing String overload. This would be
an extremely bad design choice, and the blame goes to the API
author. You've correctly mentioned that for String::format this
is harmless because the API is well-designed. We may suggest in
StringTemplate documentation that the API designers should
provide the same behavior for foo(String) and foo(StringTemplate)
when they add an overload.
I must say that we already had an experience of introducing new
interfaces in the hierarchy of widely-used library classes.
Closable got AutoClosable parent, StringBuilder became
comparable, and so on. So far, the compatibility issues
introduced were tolerable. Well, probably I'm missing something
but we have preview rounds just for this purpose: to find out the
disadvantages of the approach.
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.
I think that most of the APIs will still provide String overload.
E.g., for preparing an SQL statement, it's a perfectly reasonable
scenario to have a constant string as the input. So
prepareStatement(String) will stay along with
prepareStatement(StringTemplate). And people will still be able
to use concatenation. I don't think that the absence of String <:
StringTemplate relation will protect anybody from using the
concatenation. On the other hand, if String actually implements
StringTemplate, it will be a very simple static analysis rule to
warn if the concatenation occurs in this context. If the expected
type for concatenation is StringTemplate, then something is
definitely wrong. Without 'String implements StringTemplate', one
will not be able to write a concatenation directly in
StringTemplate context. Instead, String-accepting overload will
be used, and the expected type will be String, so static analyzer
will have to guess whether it's dangerous to use the
concatenation here. In short, I think that it's actually an
advantage: we have an additional hint here that concatenation is
undesired. Even compilation warning could be possible to implement.
So, I don't see these points as real disadvantages. I definitely
like this approach much more than adding any kind of implicit
conversion or another literal syntax, which would complicate the
specification much more.
With best regards,
Tagir Valeev.