December 14, 2019
On 2019-12-14 09:36, Walter Bright wrote:

> I've thought about this for the last week. The most practical idea is to simply concatenate adjacent strings, as in:
> 
>    database.exec(i"UPDATE %table SET key=%key, value=%{f}value "
>                    "WHERE index < %{d}index AND "
>                    "timestamp > %{D}lastTime");
> 
> I.e. the 'i' string comes first, and gets concatenated with any following string literals. This also enables using 'q' strings as interpolated strings:
> 
>      i"" q{a + b}

Implicit string concatenation is deprecated and gives an error.

-- 
/Jacob Carlborg
December 14, 2019
On 2019-12-14 10:10, Walter Bright wrote:
> I find your proposal a little confusing. By turning the string into an object, it doesn't work with printf?

One would need to explicitly call `toString` or add an `alias this` to the struct.

-- 
/Jacob Carlborg
December 14, 2019
On 2019-12-14 10:21, Walter Bright wrote:

> That doesn't work because semantic analysis will convert it into a tuple.

Yes, I understand that. I don't think it's a good idea. I wrote how I think it should work instead

-- 
/Jacob Carlborg
December 14, 2019
On Saturday, 14 December 2019 at 09:10:47 UTC, Walter Bright wrote:
> I find your proposal a little confusing. By turning the string into an object, it doesn't work with printf?

Right, not directly. But then it is trivial to add a member of the object that transforms it into a tuple suitable for printf.

So the end user's code now looks like: `printf(i"foo %bar".asFormatTuple);`

BTW I am not opposed to your version either, since it can be transformed into something close to my version. My big concern is that your version (as well as Jonathan Marler's) loses information very easily.

printf(i"%item"); // happens to work
writefln(i"%item"); // also happens to work

writeln(i"%item"); // oops! not going to do what you want

format(); // works
text(); // wrong

And there's no compile error for incorrect usage. With an object, we get existing type checking safety and can guide it to the correct usage:

printf(i""); // error cannot implicitly convert __d_interpolated_string to const char*

writefln(i""); // static assert failed: "use i"".asFormatTuple instead

writeln(i""); // happens to just work because it calls toString

> I'm also not sure it's a good idea to make this more powerful. As a simple "replace the string with a tuple" its very easy to understand.

Objects are easy to understand too. And easy to implement: on the compiler it is basically just `X!(your version)` instead of just plain `(your version)`.
December 14, 2019
On 12/14/19 3:56 AM, Walter Bright wrote:
> On 12/11/2019 2:49 PM, Steven Schveighoffer wrote:
>> Only thing better would be some way to set the default specifier to avoid all the verbosity.
> 
> Since that would require some extra syntax for each i string literal, it's not worth it.
> 

It doesn't actually

i"$apples and $bananas" would still work. The default specifier is optional, just like format specifier parameters e.g. %02x have an optional 02.

The only time extra syntax would be required is if you start with a brace and want to keep the default.

i`{"name" : $name}`.format; // JSON interpolated object.

This would result in a confusion because of the initial brace. (I'm assuming here that we can use all the different quote types for convenience)

BUT

1. You can just do
  i`{}{"name": $name}`.format;
  i` {"name": $name}`.format;  // taking advantage of the JSON grammar.
  i`{%s}{"name": $name}`.format; // respecify default
2. Or if we have your proposal of allowing string concatenation (I'll respond to that separately), then (i"{}" `{"name": $name}`).format should work, and doesn't look terrible.

Normally an interpolated string will not have any need for the default alteration, and you won't notice it.

I have some SQL strings with 10 or more fields, which would be super-verbose to continually put {?} or to maybe forget one!

If there is a better default specifier sequence that you would accept, that would be fine too.

-Steve
December 14, 2019
On 12/14/19 3:51 AM, Walter Bright wrote:

> It's a great point. I propose a modification:
> 
> Change the `{FormatString}` from being a suffix to `%` to being the whole format string, i.e. instead of `%{d}` make it `%{%d}`.
>

...

> 
> I've decided to change it to $. That looks nice. The \ ones just look awful, and  {} leaves open the problem of where to put the user-specified format.

Thanks! That's much better

-Steve
December 14, 2019
On 12/14/19 3:36 AM, Walter Bright wrote:
> On 12/11/2019 11:33 AM, H. S. Teoh wrote:
>> 2) The previous example does bring up another issue: is there a nice way
>> to handle long interpolated strings, i.e., wrap long interpolated
>> strings to multiple lines?  I.e., does the following work?
>>
>>     database.exec(i"UPDATE %table SET key=%key, value=%{f}value "~
>>             "WHERE index < %{d}index AND "~
>>             "timestamp > %{D}lastTime");
> 
> No. The trouble happens with how semantic analysis is done - leaves first, then non-leaves. It's just the wrong order to make the concatenation make sense.

What if the strings are all interpolated strings? e.g.:

database.exec(i"UPDATE %table SET key=$key, value=${f}value "~
			i"WHERE index < ${d}index AND "~
			i"timestamp > ${D}lastTime");

I don't know enough about the grammar/implementation to know if this makes more sense or not.

> 
> I've thought about this for the last week. The most practical idea is to simply concatenate adjacent strings, as in:
> 
>    database.exec(i"UPDATE %table SET key=%key, value=%{f}value "
>                    "WHERE index < %{d}index AND "
>                    "timestamp > %{D}lastTime");

But we deprecated that because it's too error prone.

> I.e. the 'i' string comes first, and gets concatenated with any following string literals. This also enables using 'q' strings as interpolated strings:
> 
>      i"" q{a + b}

I actually think 'i' should be able to go before any string literal.
e.g.:

iq{$a + $b}
i`$a + $b`

Just like suffixes can be applied to any string literal to alter the width.

-Steve
December 14, 2019
On 12/14/19 9:23 AM, Adam D. Ruppe wrote:
> On Saturday, 14 December 2019 at 09:10:47 UTC, Walter Bright wrote:
>> I find your proposal a little confusing. By turning the string into an object, it doesn't work with printf?
> 
> Right, not directly. But then it is trivial to add a member of the object that transforms it into a tuple suitable for printf.
> 
> So the end user's code now looks like: `printf(i"foo %bar".asFormatTuple);`
> 
> BTW I am not opposed to your version either, since it can be transformed into something close to my version. My big concern is that your version (as well as Jonathan Marler's) loses information very easily.

What information does it lose?

> printf(i"%item"); // happens to work
> writefln(i"%item"); // also happens to work
> 
> writeln(i"%item"); // oops! not going to do what you want

But did what you asked? Coding is full of such dilemmas. I've actually done this many many times:

writeln("Value is %s", value);

I see the result, and fix it. This isn't difficult.

One other suggestion I had was to make the resulting string literal generated by interpolation be a derivative of a string literal. That is, it's usable wherever a string literal is, but functions that wish to overload based on that type difference can do so.

i.e. we could make the writeln call above work as expected.

> 
> format(); // works
> text(); // wrong
> 
> And there's no compile error for incorrect usage. With an object, we get existing type checking safety and can guide it to the correct usage:
> 
> printf(i""); // error cannot implicitly convert __d_interpolated_string to const char*

Hm... I prefer it just working correctly.

> 
> writefln(i""); // static assert failed: "use i"".asFormatTuple instead

Same. But why wouldn't writefln be overloaded on your object type to do the right thing?

> 
> writeln(i""); // happens to just work because it calls toString

Sure, but if you are going through the trouble to deal with format specifiers, I don't know why you'd want to support writeln out of the box but not writefln.

Not only that, but now you have to pull a lot of the library into druntime (std.format for example).

>> I'm also not sure it's a good idea to make this more powerful. As a simple "replace the string with a tuple" its very easy to understand.
> 
> Objects are easy to understand too. And easy to implement: on the compiler it is basically just `X!(your version)` instead of just plain `(your version)`.

What about the lifetime issues? An object is going to make a copy. Which may not be correct or possible.

I do like two aspects of your proposal. First is that all the information is available for further compile-time manipulation. Second is that having a specialized type allows function overloading.

-Steve
December 14, 2019
On 12/14/19 2:34 AM, Walter Bright wrote:
> On 12/11/2019 2:57 AM, Alexandru Ermicioi wrote:
>> Why not just split interpolated string into just a tuple of args & strings. For example:
>> Given: (i"I ate %apples and %bananas totalling %(apples + bananas) fruit.")
>> Is lowered to a tuple: ("I ate ", apples, " and ", bananas," totalling ", apples + bananas," fruit.")
> 
> I don't see the point of that. The user could write it that way to begin with.

Walter, this is literally the point of string interpolation. A similar response to your entire proposal could be, why not just use writeln and write out all the items?

The point is not just to put the expressions where they belong, but to be less verbose about it. The dance of quotes and commas is hard to read and hard to write.

-Steve
December 14, 2019
On Saturday, 14 December 2019 at 08:36:15 UTC, Walter Bright wrote:
> On 12/11/2019 11:33 AM, H. S. Teoh wrote:
>> 2) The previous example does bring up another issue: is there a nice way
>> to handle long interpolated strings, i.e., wrap long interpolated
>> strings to multiple lines?  I.e., does the following work?
>> 
>> 	database.exec(i"UPDATE %table SET key=%key, value=%{f}value "~
>> 			"WHERE index < %{d}index AND "~
>> 			"timestamp > %{D}lastTime");
>
> No. The trouble happens with how semantic analysis is done - leaves first, then non-leaves. It's just the wrong order to make the concatenation make sense.
>
> I've thought about this for the last week. The most practical idea is to simply concatenate adjacent strings, as in:
>
>   database.exec(i"UPDATE %table SET key=%key, value=%{f}value "
>                   "WHERE index < %{d}index AND "
>                   "timestamp > %{D}lastTime");
>
> I.e. the 'i' string comes first, and gets concatenated with any following string literals. This also enables using 'q' strings as interpolated strings:
>
>     i"" q{a + b}

One thing to note is that if string interpolation returns a struct like Adam proposed, concatenation would be possible between interpolated strings.