February 27, 2020
On Thursday, 27 February 2020 at 09:30:30 UTC, Walter Bright wrote:
> On 2/27/2020 12:27 AM, Petar Kirov [ZombineDev] wrote:
>> I'm well aware that allocation is inevitable if we want this behavior. My argument is that this behavior is so ubiquitous that not following it would be surprising to much more people, than if D didn't follow C's Usual Arithmetic Conversions rules. For example, Rust not following those conversion rules is considered a good thing,
>
> Rust does not follow C syntax at all, so nobody will reasonably expect it to have C semantics. D does follow it, it's a feature, so people will have expectations.

As shown, string interpolation in other languages (not only 'stript', as you wrote) has a so well established way of performing it that everybody will reasonably expect D to behave the same.

Said that, better having no string interpolation at all in D, if the introduced feature is not at least comparable with the solution that the others out there are using: no surprise behaviour, please!

> and there it is. But having it generate a GC allocated string is not so easy to unwind, i.e. it'll be useless with printf and generate unacceptable garbage with writefln. The extra string will always make it slow. Essentially, it'll be crippled. Making D behave like a scripting language will yield scripting performance.

It seems that the other compiled languages doing it in that way have raised no concerns at all on the above matters



February 27, 2020
On Thursday, 27 February 2020 at 09:30:30 UTC, Walter Bright wrote:
> On 2/27/2020 12:27 AM, Petar Kirov [ZombineDev] wrote:
>> I'm well aware that allocation is inevitable if we want this behavior. My argument is that this behavior is so ubiquitous that not following it would be surprising to much more people, than if D didn't follow C's Usual Arithmetic Conversions rules. For example, Rust not following those conversion rules is considered a good thing,
>
> Rust does not follow C syntax at all, so nobody will reasonably expect it to have C semantics. D does follow it, it's a feature, so people will have expectations.

I'm not sure where exactly you draw the line, but I would say that C# follows C's syntax about as much as D does. Yet it doesn't import some of the broken C semantics like implicit narrowing conversions (luckily, neither does D) and allowing mixed sign comparisons (the oldest open D issue :( [0]).

My point is that if D didn't follow the usual arithmetic conversions, much fewer newcomers would even notice compared to extremely large backlash that we may get if go with the string interpolation -> raw tuple approach.

[0]: https://issues.dlang.org/show_bug.cgi?id=259

>> while if D decided to be different than all other languages w.r.t. string interpolation,
>
> You can make it behave like all those other languages simply with:
>
>     f(format("hello $a"));
>
> and there it is. But having it generate a GC allocated string is not so easy to unwind, i.e. it'll be useless with printf and generate unacceptable garbage with writefln. The extra string will always make it slow. Essentially, it'll be crippled. Making D behave like a scripting language will yield scripting performance.

I know, I know. Though I think you misunderstood. There several ways to make printf work with zero allocations. For example:

1. Have a simple pragma(inline, true) wrapper function that will convert the distinct type to printf-style args. This wrapper function can even be named printf as it would work by virtue of function overloading. This is O(1) additional code that once written no one will need to bother with.

2. Have the new type implicitly convert to printf-style args. I think this is what Adam is proposing. While nice to have, I don't think it's necessary.

As for std.stdio.write(f)(ln), there's no reason why any garbage would need to be created, as again, a simple overload that accepts the distinct type will be able to handle it just like it would (performance-wise) with DIP1027. However, with DIP1027, only writef and writefln can be used, while write and writeln will produce wrong results (wrong according to people that have used string interpolation in other languages).

> D is a language built up from simple, orthogonal parts (or at least that is a goal). A language built from larger indivisible parts is much, much less user-adaptable.

I appreciate the sentiment. Having orthogonal features in D is important to me as well. However, I think DIP1027 falls short here because it produces a single format string and that way loses all of the structure. This is ok for printf, but not for third-party libraries and even our own std.format, as with a distinct type we won't need to parse the whole format string at all, just the individual format specifiers. In other words, a distinct type would make nothrow std.format a much more tractable problem (at least for some cases).

> An example of this is the built-in associative array, which has a series of
> fairly intractable problems as a result. Another example is the built-in
> complex type in D, which turned out to be a bad idea - a much better one is
> building it as a library type.

AFAIR, most of the problems with D's built-in AAs are that they have an extern (C) interface that relies on typeinfo. If they are fully converted to templated library types, the situation would be much better. IIRC, one of the blocking issues was that D didn't have autovivification [1] operators, so a library type wouldn't be a complete replacement without additional help from the compiler.

So in conclusion, having a distinct library-defined type (in druntime) seems the best way to go to me, as it's more flexible than raw tuples, could allow easy GC-backed conversion to string for script-like code and would offer a superset of the functionality that DIP1027 would offer, while still allowing easy (although not 100% direct) calls to printf.

[1]: https://en.wikipedia.org/wiki/Autovivification
February 27, 2020
On Thursday, 27 February 2020 at 09:34:23 UTC, Walter Bright wrote:
> On 2/26/2020 7:41 AM, Arine wrote:
>> Yah, what's unwanted about that?
>
> [snip]

You're arguing against a strawman. The other poster's comment was showing a likely problem with the (rejected) dip 1027, that our new proposal fixes.

The new proposal does not allocate a new string, so none of your points apply to it.
February 27, 2020
On Thursday, 27 February 2020 at 09:30:30 UTC, Walter Bright wrote:
> You can make it behave like all those other languages simply with:
>
>     f(format("hello $a"));

At that point, it begs the question of why even bother having string interpolation.

I'd like to imagine that most newcomers/returning veterans of D would see "D has string interpolation!" and then expect it to work similar to how it does in most other languages.

They'd expect f(format("hello %s", a)) to be replaced by a much more compact f("hello $a"), instead of some D-ism take on interpolation that doesn't really provide much in the way of the main use of the feature: easily creating formatted strings.

People more used to D will likely go "Oh, that's kind of weird yet cool" once they learn what it lowers down to, yet newcomers who aren't too caught up with the language's features will simply be annoyed and confused that it 'feels' (not necessarily is) more complicated than needed.

It appears the main focus in these dicussions are "What super super cool and niche (in comparison to string formatting) things can we do with this feature!" while mostly blind-siding that it's generally an easy-to-use, easy-to-understand feature in other languages.

Similarly, as far as I can tell even with the adjustment of making these strings their own special type, something as simple (for other languages as):

```
void f(string a, string b)
{ /*...*/ }

int foo = 20;
string bar = "lalafell";
f(i"You are a foo($foo)", i"I am not a $bar");
```

Isn't easily achieved without use of an extra helper function such as i"...".format or .str, etc.

My view on it is that this is generally a QoL feature, which some languages (C#, Swift) have had success in keeping the basic principle of interpolation while also granting user code the power to perform some of the things being suggested (e.g. In C#, string interpolation works as-expected, yet check out functions such as FromSqlInterpolated[1] which can directly access the format string & values simply by taking a special `FormattableString` type)

To be honest, if D's take on interpolation makes people scratch their head in confusion at the most basic use case, then it's probably not even worth adding to the language.

[1] https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationalqueryableextensions.fromsqlinterpolated?view=efcore-3.1
February 28, 2020
On 28/02/2020 3:47 AM, SealabJaster wrote:
> Similarly, as far as I can tell even with the adjustment of making these strings their own special type, something as simple (for other languages as):
> 
> ```
> void f(string a, string b)
> { /*...*/ }
> 
> int foo = 20;
> string bar = "lalafell";
> f(i"You are a foo($foo)", i"I am not a $bar");
> ```
> 
> Isn't easily achieved without use of an extra helper function such as i"...".format or .str, etc.

What you have suggested is a GC only language feature.

This will cut out numerous use cases for D, and will leave those people swearing and complaining.

Unlike new users who may not discover this feature for a while, existing users will complain and will not be happy guranteed.

This is something we as a community want to avoid.
February 27, 2020
On Thursday, 27 February 2020 at 14:32:29 UTC, Petar Kirov [ZombineDev] wrote:
> 2. Have the new type implicitly convert to printf-style args. I think this is what Adam is proposing. While nice to have, I don't think it's necessary.

You can read my document for more detail

https://github.com/dlang/DIPs/pull/186

But basically

writefln(i"hi $name, you are visitor ${%2d}(count)");

gets turned into:

writefln(
   // the format string is represented by this type
   new_type!("hi ", spec(null), ", you are visitor ", spec("%2d"))(),
   // then the referenced arguments are passed as a tuple
   name,
   count
)


So very, very, very similar to Walter's proposal, just instead of the compiler generating the format string as a plain string, the format string is represented by a new type, defined by the spec and implemented by druntime. As a result, no more guess work - it is clear that this is meant to be interpreted as a format string. It is clear which parts are placeholders/specifiers for which arguments.
February 27, 2020
On 2/27/20 9:32 AM, Petar Kirov [ZombineDev] wrote:
>> An example of this is the built-in associative array, which has a series of
>> fairly intractable problems as a result. Another example is the built-in
>> complex type in D, which turned out to be a bad idea - a much better one is
>> building it as a library type.
> 
> AFAIR, most of the problems with D's built-in AAs are that they have an extern (C) interface that relies on typeinfo. If they are fully converted to templated library types, the situation would be much better. IIRC, one of the blocking issues was that D didn't have autovivification [1] operators, so a library type wouldn't be a complete replacement without additional help from the compiler.

We're going very off topic here, but I wanted to address this.

Large hidden invisible types are not the problem (look at normal dynamic arrays, the large hidden type built into the runtime is a huge success I think). The problem is that the compiler gives special features to such types.

In the case of AA, the ONLY missing piece that cannot be implemented by user types is this:

int[string][string] aalist;

aalist["hello"]["world"] = 5;

In essence, the compiler knows how to make this work. The operators available do not allow this expression to be captured properly by user code (i.e. we don't have opIndexIndexAssign or opIndexIndexIndexAssign, nor does that scale).

I believe the last person to try and implement a full template type that could replace AAs is H. S. Teoh. He would have a better explanation (and possibly contradict me, I don't know).

Other than that, we've ripped out all other "magic" into templates in the language. If we could get that one feature (not sure how to do this in a scalable way), I think we have a full library type that can be further factored out of the compiler. We might even be able to avoid using TypeInfo, and make CTFE AAs compatible with runtime ones.

-Steve
February 27, 2020
On Thursday, 27 February 2020 at 14:47:55 UTC, SealabJaster wrote:
> At that point, it begs the question of why even bother having string interpolation.

I encourage you to read my document too:

https://github.com/dlang/DIPs/pull/186

It addresses all these concerns. Walter's proposal is dead. It has been formally rejected. We shouldn't waste more time talking about it.

> I'd like to imagine that most newcomers/returning veterans of D would see "D has string interpolation!" and then expect it to work similar to how it does in most other languages.

My proposal doesn't work exactly like in other languages - it is uniquely D so we don't waste our potential. It does everything C# can do, except the implicit conversion to string... BUT, if you assume it is the same as other languages, you get a compile error, and the error message tells you how to convert it to a regular string!

f(i"hello $a");

error: cannot implicitly convert argument of type interpolated tuple to type string. Tip: use `.idup` to explicitly convert it to string.

f(i"hello $a".idup); // works almost like other languages now!

f2(i"hello $a", i"hello $b"); // type mismatch error, try idup

f2(i"hello $a".idup, i"hello $b".idup); // works


So education wise, it is only a few seconds: if you use it like in other languages, it doesn't work, but the compiler tells you how to fix it.

If you find the error or `.idup` method unacceptable, then... I'm sorry, but you aren't going to win that one. We've already argued this at length and the community is not willing to lose what we'd lose (ref, scope, compile-time usage, and more) by making that work; the trade-offs are too steep.
February 27, 2020
On Thursday, 27 February 2020 at 15:12:23 UTC, Adam D. Ruppe wrote:
> error: cannot implicitly convert argument of type interpolated tuple to type string. Tip: use `.idup` to explicitly convert it to string.

Oh, that pretty much sorts out my problem there, sorry if I glanced over it being mentioned previously.

> We've already argued this at length and the community is not willing to lose
> what we'd lose (ref, scope, compile-time usage, and more)

And that also explains to me why having the values packed into a struct (e.g. C# FormattableString) wouldn't be acceptable either. Again, sorry if I missed it being mentioned before.

My main other argument was the possible complexity/bugginess of having functions that provide special support for these strings, as from Walter's DIP it seemed each function would have to implement their own parsing of the format string. But I see your DIP already addresses that (_d_interpolated_format_spec("")).

I'm kind of struggling to see now why your changes were so vehemently rejected now.

Other than that, just pretend my misinformed post doesn't exist.
February 27, 2020
On Thursday, 27 February 2020 at 15:11:07 UTC, Steven Schveighoffer wrote:
> [snip]
>
> We're going very off topic here, but I wanted to address this.
>
> Large hidden invisible types are not the problem (look at normal dynamic arrays, the large hidden type built into the runtime is a huge success I think). The problem is that the compiler gives special features to such types.
>
> In the case of AA, the ONLY missing piece that cannot be implemented by user types is this:
>
> int[string][string] aalist;
>
> aalist["hello"]["world"] = 5;
>
> [snip]

Thanks for writing that. I spent a few minutes reading about autovivification and was a little unsure of what the problem was as D's operator overloading is pretty flexible. However, I don't think I've ever used or seen used multi-dimensional associative arrays. It looks as if you cannot make use of aalist["hello", "world"] and have to use it like aalist["hello"]["world"].