December 14, 2019
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}
December 14, 2019
On 12/11/2019 12:38 PM, 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);
> 
> which is not correct SQL syntax.

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}`. Then, your example becomes:

  query("select * from sometable where date < %{?}someDate");

which gets rewritten to:

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

and voila, any format specifier can be used. Robert brought up an additional problem:

  printf("%.*s", length, ptr);

as not being representable as an interpolated string. But this change will allow it to work:

  printf(i"%{%.*}length%{s}ptr");

The default format will remain `%s`, as I expect that will be the vast bulk of uses.


> 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.

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.


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

Lowering it to a tuple is good enough. You can do whatever you wish with the resulting tuple (like pass it to a template).
December 14, 2019
On 12/11/2019 2:37 PM, H. S. Teoh wrote:
> 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);

I agree, except don't need the extra ( ).
December 14, 2019
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.

December 14, 2019
On 12/13/2019 1:17 AM, Robert M. Münch wrote:
> Yes, much better to reduce syntax-cluttering. Why not lean towards existing syntax from templates:
> 
> i(?)"INSERT INTO mytable VALUES ($name, $serial, $cost)"
> 
> or even make it more configurable to catch more cases:
> 
> i($,?)"INSERT INTO mytable VALUES ($name, $serial, $cost)"
> i(#,?)"INSERT INTO mytable VALUES (#name, #serial, #cost)"
> 
> i(#,?,$,??)"INSERT INTO mytable VALUES (#name, $serial, #cost)"

Too complex for too little gain.

December 14, 2019
I find your proposal a little confusing. By turning the string into an object, it doesn't work with printf?

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.
December 14, 2019
On 12/11/2019 9:07 PM, Jonathan Marler wrote:
> I also don't see any mention of my proposal in the DIP in "Prior Work"

Indeed that is my fault and I'll fix that.
December 14, 2019
On 12/13/2019 5:11 AM, Jacob Carlborg wrote:
> * The type of an interpolated string should be the same as a regular string. That means that the following should work:
> 
> auto foo = 3;
> auto b = i"%foo";
> static assert(is(typeof(b) == string));

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


> * It should support both interpolating expressions and, with a shorter syntax, symbols, example:
> 
> auto foo = 3;
> auto bar = 4;
> auto a = i"%foo" // interpolation of symbol
> auto b = i"%(foo + bar)" // interpolation of expression

It only makes sense if foo is an expression.

> * It should not be tied to specific functions, like `printf` or `writef`

It works with anything that accepts a tuple.

> * It needs to be possible to identify which part is a literal string and which part is an interpolation

I agree that is the whole point, and the proposal does that.

> * A way to customize how the interpolation is performed. This I'm not sure how to do best. There are a couple of alternatives. In Swift string interpolation is lower to method calls [1]:
> 
> "The time is \(time)."
> 
> Is lowered to something like:
> 
> var interpolation = MyString.StringInterpolation(literalCapacity: 13,
> interpolationCount: 1)
> 
> interpolation.appendLiteral("The time is ")
> interpolation.appendInterpolation(time)
> interpolation.appendLiteral(".")
> 
> MyString(stringInterpolation: interpolation)

The interpolation just creates a tuple. What the user does with the tuple is up to him.
December 14, 2019
On Saturday, 14 December 2019 at 08:00:41 UTC, Walter Bright wrote:
> I've decided to replace the % with $. Then, % just becomes an ordinary character.
>
> Using $ also has the advantage of visually distinguishing a % format string from an interpolated format string.

Nice. It’s also more like what several other languages do.

Bastiaan.
December 14, 2019
On Wednesday, 11 December 2019 at 09:52:21 UTC, 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

If a special type is used for the format string after lowering, it can work with printf and other functions can use the format string at compile time:

struct __Interpolate
{
    immutable char *fmt;
    alias fmt this;
}

struct __InterpolateT(string fmt_)
{
    enum fmt = fmt_;
    enum __Interpolate interpolate = __Interpolate(fmt_);
    alias interpolate this;
}

void main()
{
    // printf can be used, because __InterpolateT converts to immutable(char)*:
    import core.stdc.stdio;
    printf(__InterpolateT!"test printf %d\n"(), 123);

    // Functions can detect an interpolated string using an overload:
    void writef(T...)(__Interpolate fmt, T params)
    {
        printf(fmt.fmt, params);
    }
    writef(__InterpolateT!"test writef %d\n"(), 123);

    // Functions can interpret the format string at compile time:
    void writefct(string fmt, T...)(__InterpolateT!fmt interpolate, T params)
    {
        pragma(msg, "format string at compile time ", fmt);
        printf(fmt, params);
    }
    writefct(__InterpolateT!"test writefct %d\n"(), 123);
}