Update on String Templates (JEP 459)

forax at univ-mlv.fr forax at univ-mlv.fr
Wed Mar 13 22:00:57 UTC 2024


----- Original Message -----
> From: "Guy Steele" <guy.steele at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Brian Goetz" <brian.goetz at oracle.com>, "amber-spec-experts" <amber-spec-experts at openjdk.org>
> Sent: Wednesday, March 13, 2024 10:04:46 PM
> Subject: Re: Update on String Templates (JEP 459)

>> On Mar 13, 2024, at 4:34 PM, Remi Forax <forax at univ-mlv.fr> wrote:
>> 
>> ----- Original Message -----
>>> From: "Guy Steele" <guy.steele at oracle.com>
>>> To: "John Rose" <john.r.rose at oracle.com>
>>> Cc: "Tagir Valeev" <amaembo at gmail.com>, "Brian Goetz" <brian.goetz at oracle.com>,
>>> "amber-spec-experts"
>>> <amber-spec-experts at openjdk.org>
>>> Sent: Wednesday, March 13, 2024 9:13:30 PM
>>> Subject: Re: Update on String Templates (JEP 459)
>> 
>>>> On Mar 13, 2024, at 3:33 PM, John Rose <john.r.rose at oracle.com> wrote:
>>>> 
>>>> On 9 Mar 2024, at 3:48, Tagir Valeev wrote:
>>>> 
>>>>> The idea is interesting. There's a thing that disturbs me though.
>>>>> Currently, proc."string" and proc."string \{template}" are uniformly
>>>>> processed, and the processor may not care much about whether it's a string
>>>>> or a template: both can be processed uniformly. After this change, removing
>>>>> the last embedded expression from the template (e.g., after inlining a
>>>>> constant) will implicitly change the type of the literal from
>>>>> StringTemplate to String. This may either cause a compilation error, or
>>>>> silently bind to another overload which may or may not behave like a
>>>>> template overload with a single-fragment-template. For API authors, this
>>>>> means that every method accepting StringTemplate should have a counterpart
>>>>> accepting String. The logic inside both methods would likely be very
>>>>> similar, so probably both will eventually call a third private method. For
>>>>> API user, it could be unclear how to call a method accepting StringTemplate
>>>>> if I have simple string in hands but there's no String method (or it does
>>>>> slightly different thing due to poor API design). Should I use some ugly
>>>>> construct like "This is a string but the API wants a template, so I append
>>>>> an empty embedded expression\{""}"?
>>>> 
>>>> This is a huge thread that I hesitate to dive into, but here’s me putting in one
>>>> toe:  Why do we care so much about no-arg string templates?  It’s a small
>>>> corner case!  The workarounds (for the no-arg case) are totally straightforward
>>>> even if the string template literals (as a syntax) are required to have at
>>>> least one argument.
>>>> 
>>>> Can we have a plausible use case, please, for why a ST with no arguments would
>>>> be important, so important that we are motived to invent a sigil syntax or
>>>> special type system rules, to avoid requiring the user to invoke a static
>>>> factory?
>>>> 
>>>> Also, Tagir’s workaround of adding a fake argument looks like it would work just
>>>> fine, of course depending on which processor was eventually used.
>>>> 
>>>> And in that vein let me add one new (very bike-sheddy) suggestion before I beat
>>>> a hasty retreat:  Instead of in (1) a sigil before the quote like Guy’s
>>>> $"hello", put it (1b) after the quote, and in the ST case only.  The ST syntax
>>>> could explicitly allow that a no-arg string template would be spelled with a
>>>> leading sequence "\{}... which looks like the coder started writing a ST
>>>> argument, but in fact dropped it.  So "hello" is a 5-char string, in any
>>>> context.  And "\{}hello" is a 5-char no-arg string template, in any context.
>>>> That’s Tagir’s workaround, elevated a bit into a new corner case of (existing)
>>>> syntax.
>>>> 
>>>> But even that teeny bit of syntax strikes me as overkill, because I don’t see
>>>> the importance of the use cases (no-arg STs) it helps.  Just call
>>>> ST.of("hello") and call it a day.
>>>> 
>>>> In any case, it seems fine to let the IDE take the lead with no-arg STs, helping
>>>> the user decide when and how to disambiguate strings from no-arg STs.  Putting
>>>> in syntax or type system help for this is surely more expensive than punting to
>>>> the IDE, unless there is going to be heavy use of no-arg STs for some use cases
>>>> I am not seeing.
>>> 
>>> Well, just off the top of my head as a thought experiment, if I had a series of
>>> SQL commands to process, some with arguments and some not, I would rather write
>>> 
>>> SQL.process($”CREATE TABLE foo;”);
>>> SQL.process($”ALTER TABLE foo ADD name varchar(40);”);
>>> SQL.process($”ALTER TABLE foo ADD title varchar(30);”);
>>> SQL.process($”INSERT INTO foo (name, title) VALUES (‘Guy’, ‘Hacker’);”);
>>> SQL.process($”INSERT INTO foo (name, title) VALUES (\{other name}, \{other
>>> job});”);
>>> 
>>> than
>>> 
>>> SQL.process(ST.of(”CREATE TABLE foo;”));
>>> SQL.process(ST.of(”ALTER TABLE foo ADD name varchar(40);”));
>>> SQL.process(ST.of(”ALTER TABLE foo ADD title varchar(30);”));
>>> SQL.process(ST.of(”INSERT INTO foo (name, title) VALUES (‘Guy’, ‘Hacker’);”));
>>> SQL.process(”INSERT INTO foo (name, title) VALUES (\{other name}, \{other
>>> job});”);
>>> 
>>> especially if I thought that maybe down the road I might want to change the
>>> constants 30 and 40 and ‘Hacker' to variables. I don't want to have to keep
>>> adding and deleting calls to ST.of as I edit the template strings during
>>> program development to have different numbers of interpolated expressions.
>> 
>> Given what Maurizio said and this, i think the only missing piece in the puzzle
>> is what about existing methods taking a String as parameter.
>> 
>> We know that for SQL.process(), we do not want process() to take a String but
>> only a StringTemplate.
>> But what about the existing methods that takes a String.
>> 
>> Given a method Logger.warning(String), should
>>  LOG.warning($”CREATE TABLE foo;”);
>>  LOG.warning($”INSERT INTO foo (name, title) VALUES (\{other name}, \{other
>>  job});”);
>> 
>> be legal ? Is there an auto-conversion (a kind of boxing conversion) from
>> StringTemplate to String ?
> 
> In my proposal, the answer would be “no”. Instead you would have two choices:
> 
> (1) Instead of string template expressions as in the example just given, you
> could use string literals or string interpolation expressions (omit the “$”
> characters):
> 
> LOG.warning(”CREATE TABLE foo;”);
> LOG.warning(”INSERT INTO foo (name, title) VALUES (\{other name}, \{other
> job});”);
> 
> (2) If instead you have some other sort of expression (such as a variable) whose
> type is StringTempate, you can write
> 
> LOG.warning(String.of(myStringTemplate));
> 
> This makes quite explicit that a conversion is happening from StringTemplate to
> String.

Make sense, i like it.

(1) make the string interpolation explicit, and it can be fully optimize using an invokedynamic (same trick as STR."...")

(2) The current method is interpolate(), so
  LOG.warning(myStringTemplate.interpolate());

Compared to the previous iteration, no Processor interface, no weird calling syntax but instead two new literals, string interpolation and string template.
I think the only missing optimization was FMT."..." but it can be done if necessary by specializing String.format(StringTemplate) at the compiler level.

Rémi


More information about the amber-spec-observers mailing list