----- Original Message ----- > From: "Guy Steele" <[email protected]> > To: "Remi Forax" <[email protected]> > Cc: "Brian Goetz" <[email protected]>, "amber-spec-experts" > <[email protected]> > 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 <[email protected]> wrote: >> >> ----- Original Message ----- >>> From: "Guy Steele" <[email protected]> >>> To: "John Rose" <[email protected]> >>> Cc: "Tagir Valeev" <[email protected]>, "Brian Goetz" >>> <[email protected]>, >>> "amber-spec-experts" >>> <[email protected]> >>> 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 <[email protected]> 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
