September 11, 2020
On Friday, 11 September 2020 at 18:03:24 UTC, Adam D. Ruppe wrote:
> On Friday, 11 September 2020 at 17:46:22 UTC, 12345swordy wrote:
>> You have a pull request that implement this then?
>
> No, but you can copy/paste the example implementation in the dip and translate it by hand to get an idea of how it works.

I think you have a better case if you have an PR showing what you have in mind, if formal spec is not your strong suit.
September 11, 2020
On Friday, 11 September 2020 at 16:32:28 UTC, Adam D. Ruppe wrote:
> Hitting all ten is impossible; some of them are directly contradictory. But if we loosen some of those "should work" things to "works with .idup", then we actually can do them all.

If that's your acceptance criterion, it seems to me like my, Walter's, and Jonathan's proposals are all just as good, since you can achieve the same result by appending `.text` and/or `.format`.

In fact, I'd argue that they're even better than DIP 1036, because they work with the existing `text` and `format` functions rather that requiring new a tailor-made overload of `idup` to be added to druntime.


> If we go up one level and just present the parameter list, then you could still send it to an object, but you're no longer forced to, so all that data is still available. Jonathan Marler's original PR did what you proposed for `i""`. Walter Bright's did.... almost... what you proposed for `f""`. But better than any of them is Javascript's system, like I wrote about in a previous message, adapted to D.

The main difference is that the Javascript system uses a regular-old Javascript list for its "spec object", rather than an opaque runtime-defined type. That's the real sticking point for me. Especially since the only features it actually buys you that you can't get easily with any of the naked-argument-list proposals are:

- Implicit conversion to const(char)*
- Ability to write custom overloads that supply a default format specifier

I don't think those two features alone are important enough to justify adding a bunch of extra cruft to Phobos and druntime--not to mention giving up compatibility with third-party libraries that *don't* add custom interpolation-spec overloads.
September 11, 2020
On Friday, 11 September 2020 at 17:37:36 UTC, SHOO wrote:
> 2. It doesn't seem to work well with the {} style formatting employed by C#, Rust, etc. Is the following interpretation correct?
> `String.Format("You are now {0} years old.", years - 1)`

You'd do it more like

`String.Format(i"You are now ${{0}}(years - 1) years old.")`

No backslashes. But it isn't really ideal for positional ones like that since you'd have to keep the count going right yourself.

At this point you would ideally write an overload to String.Format that checks for the interpolation spec then translates.

The DIP's text as submitted right now makes this unnecessarily difficult, so we're gonna reword it. The same implementation still works though.

> 3. The object.idup in the DIP uses `import std.format`, is it permissible for the druntime to be dependent on Phobos?

Technically, druntime is still independent. Since it is a template, its dependency falls on the user, not the library.

When you compile druntime, that code is completely ignored. It is not present in the compiled library and the import is not processed.

If you use the idup function, the instance appears in *your* code and only then is Phobos actually imported. So formally, the dependency is on phobos from your code at the use point. So if you never use it, there's no dependency.

This is one of D's coolest features to me btw.

> 4. Are there any reasons why backquotes (i`...`) are not allowed?

Trying to just define the simplest thing that can work. D has a lot of string types and adding i to each of them is a lot.... All these cases are possible with the juxtaposition thing though:

i""`....` would work the same way.

> 5. Although you use the name idup, idup should be used to create `immutable(_d_interpolated_string!Parts)`

There's two pieces to the interpolated tuple: the spec definition and the arguments. The _d_interpolated_string!Parts thing is just the spec definition. So your example is wrong: you left the arguments out. idup makes no sense without combining it.

The main reason for using idup over toString though is just reusing an existing global name... it won't break any code since it isn't introducing anything new. And since it already works on string it has some precedent:

char[] a;
string b = a; // cannot convert, you need to idup it.

string b = i"xxx"; // cannot convert, also need to idup it.

September 11, 2020
On Friday, 11 September 2020 at 18:16:10 UTC, Paul Backus wrote:
> The main difference is that the Javascript system uses a regular-old Javascript list for its "spec object", rather than an opaque runtime-defined type.

Frankly, I hate the opaque thing too and it is going to change. In my mind, it *must* be introspectable to be useful (without that, it also fails test #8 btw).

The sample implementation in the DIP its all these points, but the spec text doesn't. We had a hard time figuring out how to word it in spec-ese  and kinda gave up, but it is obvious that giving up on that was a major mistake that we're going to go back and correct after this feedback round.
September 11, 2020
On Friday, 11 September 2020 at 18:24:25 UTC, Adam D. Ruppe wrote:
> On Friday, 11 September 2020 at 18:16:10 UTC, Paul Backus wrote:
>> The main difference is that the Javascript system uses a regular-old Javascript list for its "spec object", rather than an opaque runtime-defined type.
>
> Frankly, I hate the opaque thing too and it is going to change. In my mind, it *must* be introspectable to be useful (without that, it also fails test #8 btw).
>
> The sample implementation in the DIP its all these points, but the spec text doesn't. We had a hard time figuring out how to word it in spec-ese  and kinda gave up, but it is obvious that giving up on that was a major mistake that we're going to go back and correct after this feedback round.

Okay, here's my follow-up question:

D already has two widely-used conventions for passing "string data with other stuff in the middle" to a function. The first is to use a format string as the first argument, followed by all the other bits of "stuff" as the rest of the argument list. The second is to break the string into pieces, and insert the other bits of "stuff" into the argument list between those string pieces.

What do we gain by adopting a *third* convention from Javascript? Aren't the two we have already enough?
September 11, 2020
On Friday, 11 September 2020 at 18:32:05 UTC, Paul Backus wrote:
> What do we gain by adopting a *third* convention from Javascript?

The complexity of the format spec may seem superfluous, however it serves four key roles:

    1. It divorces the compiler entirely from details of generated format strings. For example, different functions that accept format strings might use different default format specifiers.
    2. It allows overloading existing string-accepting functions to prevent accidental usage that happens to fit the parameter list (see example below).
    3. It provides necessary error checking capabilities.
    4. It provides an additional API for user functions to introspect the string, building on D's existing compile-time capabilities.
September 11, 2020
On Friday, 11 September 2020 at 19:04:23 UTC, Adam D. Ruppe wrote:
> The complexity of the format spec may seem superfluous, however it serves four key roles:
>
>     1. It divorces the compiler entirely from details of generated format strings. For example, different functions that accept format strings might use different default format specifiers.

A much easier way to do this is to simply have no default format specifier in the first place. This is how f"..." strings would work in my proposal. (Though I agree this was an issue with DIP 1027.)

>     2. It allows overloading existing string-accepting functions to prevent accidental usage that happens to fit the parameter list (see example below).

If a function is prone to accidental usage with string interpolation, it's probably also prone to accidental usage without string interpolation. The solution is to use the type system. Again, I refer you to Scott Meyers for a more detailed explanation:

https://www.youtube.com/watch?v=5tg1ONG18H8&t=47m15s

>     3. It provides necessary error checking capabilities.

Not sure what you mean by this. The Phobos functions that use the existing argument-list conventions already do error checking; is there something they're currently missing?

>     4. It provides an additional API for user functions to introspect the string, building on D's existing compile-time capabilities.

We can already do this with existing D features. See for example `std.format.format`, which has an overload that introspects the format string at compile time.
September 11, 2020
On Friday, 11 September 2020 at 19:31:33 UTC, Paul Backus wrote:
> The solution is to use the type system.

This is exactly what the DIP does!

Something serious must have gotten lost between my brain and the text.

> Not sure what you mean by this. The Phobos functions that use the existing argument-list conventions already do error checking; is there something they're currently missing?

This is kinda a rephrasing of the other three items (though, of course, introspection can do a lot more than just error checking, like I imagine a world where we gather translation strings like gnu gettext but 100% with a stock D compiler, no need for add-on tools) - by providing a new type, you can not only catch it at function overload / template constraint level, but you can also catch errors inside the format string itself.

You said:

> We can already do this with existing D features. See for example `std.format.format`, which has an overload that introspects the format string at compile time.

Indeed, and the DIP text has this example:

https://github.com/dlang/DIPs/blob/15537f9b36afa48f1c1cd57468e8c77f8f2499dd/DIPs/DIP1036.md#usage-in-existing-string-accepting-functions

<quote>

auto writefln(Fmt, Args...)(Fmt fmt, Args args) if (isInterpolationSpec!Fmt)
        return std.stdio.writefln!(fmt.toFormatString!"%s", Args)(args);
}

These ensure that interpolated strings just work for their most likely target functions while also providing a new benefit: the format string, including user additions via ${}, will be checked at compile time, even when passed as a run-time argument.

</quote>


Look very carefully at what it is doing there... it does a compile time check of a runtime argument through its type, actually ignoring the runtime pointer to it. Since a different type `Fmt` is generated for each unique i"" instance, you can do the same compile time optimizations format does *without* the explicit overload.

It is valid to pass that format string as a template argument since it is generated from the *static data*, encoded in the type, not from the runtime string. This is also the reason why "All format specifiers must be known at compile time" is listed under Limitations.

So let's say I call

string world = "lol";
func(i"hello ${%d}world");

func's implementation is free to inspect that format string at compile time! It might forward to the `writefln!fmt(args)` in which case it actually gets to leverage existing language features in a new way to check that %d/string mismatch and report it in a way that is impossible with normal writefln today (you must use the explicit template arg overload version at the call site).

This DIP builds on D's unique strengths, enhancing existing opportunities. None of the others come close, they're just syntax sugar.
September 11, 2020
On 9/11/20 2:24 PM, Adam D. Ruppe wrote:
> On Friday, 11 September 2020 at 18:16:10 UTC, Paul Backus wrote:
>> The main difference is that the Javascript system uses a regular-old Javascript list for its "spec object", rather than an opaque runtime-defined type.
> 
> Frankly, I hate the opaque thing too and it is going to change. In my mind, it *must* be introspectable to be useful (without that, it also fails test #8 btw).

Well it looks like that opaque thing should be the result of "tuple" and what's now "idup" should be "text", which takes us somewhere in between the previous DIP (and what people opine in this thread) and this DIP.

Lowering for this:

i"Hello, $name"

would be:

tuple("Hello, ", name)

and that's the way the cookie crumbles.

Of course the problem is "tuple" is somewhere in std instead of being in object.d. But if we zero in on this notion that we want a simple lowering, no support for all that %s nonsense, and something that people don't need to design functions especially for, that's an easy problem to look at.

BTW people will do this:

auto var = i"Hello, $name";

They will expect a string. Now maybe we can talk them into well that's a tuple, which is nice because @nogc, has full type info, etc. etc. But if we start talking about yet another __opaqueTypeWithItsOwnAPI then we've lost already.
September 11, 2020
On Friday, 11 September 2020 at 19:59:35 UTC, Adam D. Ruppe wrote:
> On Friday, 11 September 2020 at 19:31:33 UTC, Paul Backus wrote:
>> The solution is to use the type system.
>
> This is exactly what the DIP does!
>
> Something serious must have gotten lost between my brain and the text.

I guess you didn't have time to check out the video. What I mean is, the solution is to change the signature of "createWindow":

    alias Width = Typedef!(int, int.init, "Width");
    alias Height = Typedef!(int, int.init, "Height");

    void createWindow(string title, Width width, Height height);

This solves the issue of accidentally passing the wrong thing with i"Window $id", and also prevents non-interpolation-related accidental usage, like mixing up the width and height.

> This DIP builds on D's unique strengths, enhancing existing opportunities. None of the others come close, they're just syntax sugar.

I agree with this 100%, except I think it's a point against DIP 1036 and in favor of the other proposals. :)

We already have ways to put stuff in the middle of strings. The reason we want string interpolation is to make the syntax for it less ugly. So, it *should* be "just syntax sugar." Anything beyond that is bloat.