December 11, 2019
On 12/11/19 4:52 AM, Mike Parker wrote:
> This is the feedback thread for the first round of Community Review for DIP 1027, "String Interpolation":
> 
> https://github.com/dlang/DIPs/blob/148001a963f5d6e090bb6beef5caf9854372d0bc/DIPs/DIP1027.md 
> 
> 
> All review-related feedback on and discussion of the DIP should occur in this thread. The review period will end at 11:59 PM ET on December 25, or when I make a post declaring it complete.
> 
> At the end of Round 1, if further review is deemed necessary, the DIP will be scheduled for another round of Community Review. Otherwise, it will be queued for the Final Review and Formal Assessment.
> 
> Anyone intending to post feedback in this thread is expected to be familiar with the reviewer guidelines:
> 
> https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md
> 
> *Please stay on topic!*
> 
> Thanks in advance to all who participate.

First, I like the concept. It fixes the issue of "which parameters are string literals and which are variables?" issue of the straight lower-to-tuple design.

But there are a couple problems.

This is very much focused on writef and printf. What about other functions that accept similar string + arg tuple, but don't use %s specifiers? Perfect example is SQL:

query("select * from sometable where date < ?", someDate);

With the interpolation string:

query(i"select * from sometable where date < %someDate");

This translates to:

query("select * from sometable where date < %s", someDate);

which is not correct SQL syntax.

This means I have to create a separate function that accepts interpolated strings (can't be the same name because it would be the same parameters for overloading!) and inside that function, I need to TRANSLATE the formatted string into something sql can understand. Not only that, but I have to do it at runtime, since query accepts a string as a runtime parameter.

Not only that, but this doesn't provide a mechanism to hook compile-time format strings at all, even though the format string is known at compile-time.

The other problem is that you are using % for the interpolated fields. This is quite puzzling, considering that the main target (printf and writef) uses % as the format specifier. Why not just change the specifier to $ or {} or \() or \{} or literally any of the other interpolation systems out there. Then you don't need the %%%% mentioned elsewhere to make one percentage sign.

If we can think of a way to hook the string generation and have the parameters come afterwards, that would be ideal.

For example, if you lowered (and I'm going to use a better format specifier here):

i"I ate \({d}apples) and \(bananas) totaling \(apples + bananas) fruit."

to

InterpStr!("I ate ", FormatSpec!("d"), " and ", FormatSpec!(""), " totaling ", FormatSpec!(""), " fruit."), apples, bananas, apples + bananas

Where InterpStr and FormatSpec were defined by the library to be whatever Druntime specified, then you could handle any cases, and provide the most . For example, writef could accept the type as the first parameter, and translate easily into a standard writef call, or avoid having to parse the format specifiers all together!

This wouldn't work with printf. But I'm not so sure that's a huge problem. One could easily wrap printf in something D-ish.

Again, like the concept, but it's too narrowly focused on printf and writef.

-Steve
December 11, 2019
On Wednesday, 11 December 2019 at 20:28:21 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 11 December 2019 at 19:15:18 UTC, Meta wrote:
>> The main sticking point is that you have to write mixin(interp!"....") so the symbols will be looked up in the proper scope. Unfortunately, that also means that the above syntax is the best a library solution can do, currently.
>
> Maybe "mixin" could be a return type requiring CTFE and re-evaluated in the calling context?  A bit dangerous perhaps.

Yeah, I think if it comes down to adding a new language feature to the compiler, or changing how mixins behave in such a way that may be cause for security concerns, it's less costly to add the language feature.

Really, though, all that's needed is to allow mixin templates to be mixed in without using the `mixin` keyword (I say "all", but that's quite a large language change):

string interp(string s)()
{
    ....
}

mixin template i(string s)
{
    enum i = mixin(interp!s);
}

auto n = 2, m = 5;
auto s = i!"The product of ${n} and ${m} is ${n * m}";
writefln(s);
December 11, 2019
On Wednesday, 11 December 2019 at 20:46:03 UTC, Meta wrote:
> Really, though, all that's needed is to allow mixin templates to be mixed in without using the `mixin` keyword (I say "all", but that's quite a large language change):

Could one option be to use another symbol than «!» and make it imply the mixin?

… f = some_formatter_that_builds_a_string …;

immutable s = f!!"My name is {this.name}";

December 11, 2019
On Wednesday, 11 December 2019 at 20:38:49 UTC, Steven Schveighoffer wrote:
>
> This is very much focused on writef and printf. What about other functions that accept similar string + arg tuple, but don't use %s specifiers? Perfect example is SQL:
>
> query("select * from sometable where date < ?", someDate);
>
> With the interpolation string:
>
> query(i"select * from sometable where date < %someDate");
>
> This translates to:
>
> query("select * from sometable where date < %s", someDate);
>
> 

query(i"select * from sometable where date < %someDate".format);







December 11, 2019
On 12/11/19 3:59 PM, Andrea Fontana wrote:
> On Wednesday, 11 December 2019 at 20:38:49 UTC, Steven Schveighoffer wrote:
>>
>> This is very much focused on writef and printf. What about other functions that accept similar string + arg tuple, but don't use %s specifiers? Perfect example is SQL:
>>
>> query("select * from sometable where date < ?", someDate);
>>
>> With the interpolation string:
>>
>> query(i"select * from sometable where date < %someDate");
>>
>> This translates to:
>>
>> query("select * from sometable where date < %s", someDate);
>>
> 
> query(i"select * from sometable where date < %someDate".format);

https://en.wikipedia.org/wiki/SQL_injection

Besides, this is bad for performance, you are translating a type to a string, just to send it to the server, to have it translate it back into the type. Sending the type directly is much more efficient.

What I want is what I first typed, just in a nicer format. D has such great power at translating code, it should be able to do this. I don't see why we would settle for something that basically is a "nicer" writef or format when it could be used everywhere.

-Steve
December 11, 2019
On Wed, Dec 11, 2019 at 03:38:49PM -0500, Steven Schveighoffer via Digitalmars-d wrote: [...]
> But there are a couple problems.
> 
> This is very much focused on writef and printf. What about other functions that accept similar string + arg tuple, but don't use %s specifiers? Perfect example is SQL:
> 
> query("select * from sometable where date < ?", someDate);
> 
> With the interpolation string:
> 
> query(i"select * from sometable where date < %someDate");
> 
> This translates to:
> 
> query("select * from sometable where date < %s", someDate);
> 
> which is not correct SQL syntax.
[...]

Here's a potential 2-step fix:

1) Change the interpretation of `%({X}varname)` to mean "use `X` instead
of %s as placeholder in output string", rather than "use `%X` ...".
I.e.:

	i"%abc"		== tuple("%s", abc);
	i"%({%x}abc)"	== tuple("%x", abc);
	i"%({?}abc)"	== tuple("?", abc); // bingo, SQL syntax!

2) Then, in light of the %%%% problem, and to fix the repeated % in "%{%x}abc", change the default metacharacter to something like $:

	i"$abc"		== tuple("%s", abc);
	i"$({%d}abc)"	== tuple("%d", abc);
	i"$({?}abc)"	== tuple("?", abc);

For convenient interop with printf/writefln, we can still default to "%s" as the default placeholder, but the {} syntax now no longer assumes printf syntax, making i"" literals MUCH more useful in many more places outside the purvey of printf/writefln.

So you'd do SQL strings like this:

	string name;
	int serial;
	float cost;
	db.exec(i"INSERT INTO mytable VALUES (${?}name, ${?}serial, ${?}cost)");

which translates the last line to:

	db.exec("INSERT INTO mytable VALUES (?, ?, ?)", name, serial, cost);

without the need for any intermediate format string parser.


T

-- 
VI = Visual Irritation
December 11, 2019
On 12/11/19 5:37 PM, H. S. Teoh wrote:
> Here's a potential 2-step fix:
> 
> 1) Change the interpretation of `%({X}varname)` to mean "use `X` instead
> of %s as placeholder in output string", rather than "use `%X` ...".
> I.e.:
> 
> 	i"%abc"		== tuple("%s", abc);
> 	i"%({%x}abc)"	== tuple("%x", abc);
> 	i"%({?}abc)"	== tuple("?", abc); // bingo, SQL syntax!
> 
> 2) Then, in light of the %%%% problem, and to fix the repeated % in
> "%{%x}abc", change the default metacharacter to something like $:
> 
> 	i"$abc"		== tuple("%s", abc);
> 	i"$({%d}abc)"	== tuple("%d", abc);
> 	i"$({?}abc)"	== tuple("?", abc);
> 
> For convenient interop with printf/writefln, we can still default to
> "%s" as the default placeholder, but the {} syntax now no longer assumes
> printf syntax, making i"" literals MUCH more useful in many more places
> outside the purvey of printf/writefln.
> 
> So you'd do SQL strings like this:
> 
> 	string name;
> 	int serial;
> 	float cost;
> 	db.exec(i"INSERT INTO mytable VALUES (${?}name, ${?}serial, ${?}cost)");
> 
> which translates the last line to:
> 
> 	db.exec("INSERT INTO mytable VALUES (?, ?, ?)", name, serial, cost);
> 
> without the need for any intermediate format string parser.

OK, this is definitely a winner, way better than my idea. Only thing better would be some way to set the default specifier to avoid all the verbosity.

Please make this part of the DIP Walter!

-Steve
December 11, 2019
On 12/11/19 5:49 PM, Steven Schveighoffer wrote:

> OK, this is definitely a winner, way better than my idea. Only thing better would be some way to set the default specifier to avoid all the verbosity.

Hm...  heredoc gives us a precedent that might be valuable here.

e.g.:

db.exec(i"{?}INSERT INTO mytable VALUES ($name, $serial, $cost)")

printf(i"{%d}I ate $apples and $bananas totaling $(apples + bananas) fruit.\n");

-Steve
December 11, 2019
On Wednesday, 11 December 2019 at 22:49:01 UTC, Steven Schveighoffer wrote:
> On 12/11/19 5:37 PM, H. S. Teoh wrote:
[…]
>> 	i"$abc"		== tuple("%s", abc);
>> 	i"$({%d}abc)"	== tuple("%d", abc);
>> 	i"$({?}abc)"	== tuple("?", abc);
>> 
>> For convenient interop with printf/writefln, we can still default to
>> "%s" as the default placeholder, but the {} syntax now no longer assumes
>> printf syntax, making i"" literals MUCH more useful in many more places
>> outside the purvey of printf/writefln.
>> 
>> So you'd do SQL strings like this:
>> 
>> 	string name;
>> 	int serial;
>> 	float cost;
>> 	db.exec(i"INSERT INTO mytable VALUES (${?}name, ${?}serial, ${?}cost)");
>> 
>> which translates the last line to:
>> 
>> 	db.exec("INSERT INTO mytable VALUES (?, ?, ?)", name, serial, cost);
>> 
>> without the need for any intermediate format string parser.
>
> OK, this is definitely a winner, way better than my idea. Only thing better would be some way to set the default specifier to avoid all the verbosity.
>
> Please make this part of the DIP Walter!

Isn’t https://forum.dlang.org/post/vxzqttydlvzngrwrvipa@forum.dlang.org better still? Streamlining template mixins could pay off in many more places, and is truly extendable and customisable.

Bastiaan
December 11, 2019
On Wednesday, 11 December 2019 at 20:57:35 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 11 December 2019 at 20:46:03 UTC, Meta wrote:
>> Really, though, all that's needed is to allow mixin templates to be mixed in without using the `mixin` keyword (I say "all", but that's quite a large language change):
>
> Could one option be to use another symbol than «!» and make it imply the mixin?
>
> … f = some_formatter_that_builds_a_string …;
>
> immutable s = f!!"My name is {this.name}";

I like this. A universal improvement that also happens to enable all kinds of string interpolation, including SQL.

Bastiaan.