December 14, 2019
On Saturday, 14 December 2019 at 15:04:24 UTC, Steven Schveighoffer wrote:
> What information does it lose?

The grouping of the items in the string. Tuples auto-flatten and then the function cannot tell any more if they actually were together to begin with.

The format string lets you reconstruct it though, assuming you know to check for a format string. This is why I can work with Walter's proposal, it would just be disappointing to lose the overloading aspect of it.

> One other suggestion I had was to make the resulting string literal generated by interpolation be a derivative of a string literal.

That's what an object with `alias toString this` is!

> Hm... I prefer it just working correctly.

We can do that with Phobos functions (and indeed anything written after we decide on the interface), but printf's interface is already locked down, so it is either just working there or having an object, can't do both.... and I think that'd be a pity since a compile error telling you to use ".asFormatString" is such an easy solution.

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

Solved problem: you'd make the method templated so it is only pulled if used. The ^^ operator in D works like this already. No need to actually bring it in to druntime, it will just reference phobos iff used.

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

So does a tuple passed to a function, there's no difference here anyway.

Remember, all my thing really boils down to is the compiler inserting X!T instead of just T.



December 14, 2019
On 12/14/2019 7:07 AM, Steven Schveighoffer wrote:
> 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?

String interpolation for format strings is quite different.


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

    "I ate ",
     apples,
    " and ",
     bananas,
    " totalling ",
     apples + bananas,
    " fruit."

isn't too bad. Formatting, though, won't change the issue with format strings and the disconnect between the arguments and their positions.

December 14, 2019
On Saturday, 14 December 2019 at 21:22:41 UTC, Walter Bright wrote:
> On 12/14/2019 7:07 AM, Steven Schveighoffer wrote:
>> 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.
>
>     "I ate ",
>      apples,
>     " and ",
>      bananas,
>     " totalling ",
>      apples + bananas,
>     " fruit."
>
> isn't too bad. Formatting, though, won't change the issue with format strings and the disconnect between the arguments and their positions.

Is format strings worth building on?

With UFCS we could create types which control formatting
"pi=", pi.dec(10),
255, " in hex ", 255.hex

December 14, 2019
On Saturday, 14 December 2019 at 21:35:51 UTC, Tove wrote:
> Is format strings worth building on?
>
> With UFCS we could create types which control formatting
> "pi=", pi.dec(10),
> 255, " in hex ", 255.hex

This is already possible, though:

  import std;

  struct HexPrinter(T) {
      T item;
      void toString(scope void delegate(string) sink, FormatSpec!char fmt) const {
          sink(format("%x", item));
      }
  }

  HexPrinter!T hex(T)(T n) {
      return HexPrinter!T(n);
  }

  void main() {
      writefln("255 = %s hex", 255.hex);
  }

so with DIP-1027 it'd remain possible with a syntax like

  writefln(i"255 = $(255.hex) hex");

December 15, 2019
On Saturday, 14 December 2019 at 07:34:05 UTC, 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.

Yes he could. The main concern I have is that current proposal is tied to printf syntax (template string first, and then args to replace) which will be quite cumbersome for people who would like to use tuple resulting from interpolation. They will need to parse template string to find all args placeholders and then split template string on those locations in most basic case. This is unnecessary boilerplate code thrown onto user. Example above solves this issue by removing the need to parse this template string since it won't be there anymore. In any case Adam's version seems more versatile and could work nicely, allowing user to choose what he would get from interpolation. For example it could alias itself to a built version of interpolated string, and expose interface to get interpolated tuple in printf format, or in format suitable for text function. Also having such structure we could encode other information such as variable prefix syntax used in interpolation string if it is decided to have the ability to change prefix, which could be then used nicely to throw asserts on wrong interpolation syntax being used and etc.

The only thing is I don't understand why a struct is not sufficient for interpolated string  in Adam's proposal.

Best regards,
Alexandru
December 14, 2019
On 12/14/2019 6:47 AM, Steven Schveighoffer wrote:
> 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");


Then you'll get a tuple that looks like:

    "UPDATE %s SET key=%s, value=%f ", key, value, "WHERE index < %d AND ", index, "timestamp > %D", lastTime

which won't work for printf or writefln.


> I actually think 'i' should be able to go before any string literal.
> e.g.:
> 
> iq{$a + $b}
> i`$a + $b`

Since the other method works, I am striving to keep things as simple as possible. There needs to be very good reasons for gratuitous multiple ways of doing something, and I don't see one here. Of course, then there's "what about qi strings", aaggghhhh.
December 14, 2019
On 12/14/2019 5:34 AM, Jacob Carlborg wrote:
> Implicit string concatenation is deprecated and gives an error.

I know. But i strings aren't string literals, and this would be special cased for that. And I wouldn't have proposed that if ~ would work, but it does not work for this.

December 14, 2019
On 12/14/2019 6:36 AM, Steven Schveighoffer wrote:
> [...]

It wouldn't be used enough to make it worthwhile. The extra syntax necessary also requires yet another means to distinguish it from the rest of the string.
December 14, 2019
On 12/14/2019 6: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);`

I was afraid that would be it. The extra syntax kills it, along with the extra import required, along with not working with #betterC.

You can still #thagomize it, or turn it into an object, etc., with:

    Object thagomize(string, ...);
    ...
    thagomize(i"foo%bar");

It's just not the default.

December 15, 2019
On Sunday, 15 December 2019 at 01:44:18 UTC, Walter Bright wrote:
> The extra syntax kills it

This is true, but ONLY for the printf case. It just works in all other cases.

> with the extra import required, along with not working with #betterC.

No need for import - this is a definition in object.d, and it will work in betterC since this is a compile-time transformation (the `asFormatString` definition is essentially just `template asFormatString(T...) { alias asFormatString = T; }`.

And my proposal also doesn't need an import to be assigned to a string whereas yours does.

string a = i""; // mine would trigger the alias toString this and just work

string a = format(i""); // must import std.format. also fails in betterC btw

> It's just not the default.

Which is usable (that's why I'm not opposed to your proposal) but means it is now impossible to catch accidental misuses at compile time. They type system no longer helps you.


But what about a small compromise: what if JUST the format string was given a new type and the rest was still a tuple. The format string can implicitly convert to the same things a string literal can; it can go to `string` or to `const char*`, but then user-defined functions are also able to overload on it.

So

i"hi %test"

becomes basically

typedef string InterpolatedFormat;

type_tuple(InterpolatedFormat("hi %s"),  test)


That'd still work in printf - it just implicitly converts back to char* - and gives just enough type information hints to cover my advanced use cases too.