February 04, 2021
On Thursday, 4 February 2021 at 16:27:32 UTC, Adam D. Ruppe wrote:
> On Thursday, 4 February 2021 at 16:15:03 UTC, Meta wrote:
>> I 100% agree with this. Reading over the thread, it seems clear that we're trying to pessimize the most common use case
>
> How often do you just create a string and not actually do anything with it?
>
> I'm very skeptical most this stuff will actually matter in practice since most cases will be used in function calls.

Your response confuses me a bit, so can you define "not actually do anything with it"? You'd do all the normal things you do with strings. Print them, parse them, replace sequences, etc.
February 04, 2021
On Thursday, 4 February 2021 at 17:00:43 UTC, Ola Fosheim Grøstad wrote:
> On Thursday, 4 February 2021 at 15:34:07 UTC, Adam D. Ruppe wrote:
>> but you don't know that the opening brace is interpolating.
>>
>> enum name = "foo";
>> mixin(iq{ void {name}() {bar} });
>>
>>
>> Is {bar} interpolation or a function body?
>
> It would be interpolation. If you are in text (string) mode when hitting "{" it would be taken as opening an expression that should be converted to string. So, no ambiguity.

This makes using interpolation to generate D code significantly more cumbersome, because '{' is a common character in D code, and every occurrence of it would have to be escaped. It is much easier if the sequence used to introduce an interpolated expression is not a valid D token.
February 04, 2021
On Thursday, 4 February 2021 at 17:04:20 UTC, Meta wrote:
> Your response confuses me a bit, so can you define "not actually do anything with it"? You'd do all the normal things you do with strings. Print them, parse them, replace sequences, etc.

So you'd pass them to functions. Which, with a mature library ecosystem, doesn't actually need anything beyond the naked tuple! It would just work without any conversions.

Heck, functions like `writeln` and `text` already just work in the POC with no implicit conversions at all, no explicit library overlaods. And you can use those to do the translation anyway when you do need it.
February 04, 2021
On Thursday, 4 February 2021 at 16:31:17 UTC, Arafel wrote:
> On 4/2/21 17:15, Meta wrote:
>> However, if you want a tuple sequence as described in the DIP, you can simply call a (probably compiler-supplied) helper method; maybe it doesn't even have to be new: why not .tupleof?
>> 
>> auto apples = 2;
>> auto bananas = 3;
>> 
>> auto s1 = i"I have ${apples + bananas} fruit";
>> static assert(is(typeof(s1) == string));
>> 
>> auto s2 = i"I have ${apples + bananas} fruit".tupleof;
>> 
>> Then we can hide all the complexity of interpolated sequences behind the .tupleof (or whatever we decide on) magic property.
>
> Could this be made so it's transparent to the user of a library?
>
> I.e. in the SQL case, the user of a library just types:
>
> ```
> auto result = connection.execute(i"SELECT * FROM foo WHERE bar = ${baz}");
> ```
>
> instead of
>
> ```
> auto result = connection.execute(i"SELECT * FROM foo WHERE bar = ${baz}".tupleof);
> ```
>
> If it has to be explicit, how would the signature of Connection.execute(...) look like? How would the user easily know that the function expects an interpolated literal, just by looking at it?

When Adam D. Ruppe wrote about SQL injections, I thought about that. The signature of `execute` must not take a string as the only parameter for sure. This is the basic pattern for accepting an interpolated sequence.

    auto execute(string str, Args...)(interp!str first, Args rest) { }

If you call `execute` with `execute(i"SELECT ${name} FROM table"), the compiler tries i"SELECT ${name} FROM table".tupleof and finds a PERFECT match to the above template. If that `execute` is the only one available, you cannot use it with something other than an interpolated string as the first parameter; nothing else (like a regular string literal) will match `interp!str`.
February 04, 2021
On Thursday, 4 February 2021 at 17:39:50 UTC, Q. Schroll wrote:
> On Thursday, 4 February 2021 at 16:31:17 UTC, Arafel wrote:
>> [...]
>
> When Adam D. Ruppe wrote about SQL injections, I thought about that. The signature of `execute` must not take a string as the only parameter for sure. This is the basic pattern for accepting an interpolated sequence.
>
>     auto execute(string str, Args...)(interp!str first, Args rest) { }
>
> If you call `execute` with `execute(i"SELECT ${name} FROM table"), the compiler tries i"SELECT ${name} FROM table".tupleof and finds a PERFECT match to the above template. If that `execute` is the only one available, you cannot use it with something other than an interpolated string as the first parameter; nothing else (like a regular string literal) will match `interp!str`.

@everyone:
We discuss more in depth on Discord. If you want to you could join the server and flesh out your ideas there. Easier to clarify various concepts imo.
February 04, 2021
On Thursday, 4 February 2021 at 17:14:31 UTC, Adam D. Ruppe wrote:
> On Thursday, 4 February 2021 at 17:04:20 UTC, Meta wrote:
>> Your response confuses me a bit, so can you define "not actually do anything with it"? You'd do all the normal things you do with strings. Print them, parse them, replace sequences, etc.
>
> So you'd pass them to functions. Which, with a mature library ecosystem, doesn't actually need anything beyond the naked tuple! It would just work without any conversions.
>
> Heck, functions like `writeln` and `text` already just work in the POC with no implicit conversions at all, no explicit library overlaods. And you can use those to do the translation anyway when you do need it.

I think it comes down to, when people (primarily and especially new users) see "", they expect a string. To get a weird (to them) tuple thing instead is just not a good user experience. Even if libraries can be made to work with "auto-tuple" strings, I really don't think we should be asking them to participate in this façade; it's an illusion that breaks on cursory inspection.

IMO it should be strings first, tuples if you ask.
February 04, 2021
On Thursday, 4 February 2021 at 17:49:06 UTC, Imperatorn wrote:

> @everyone:
> We discuss more in depth on Discord. If you want to you could join the server and flesh out your ideas there. Easier to clarify various concepts imo.

Which channel?
February 04, 2021
On Wednesday, 3 February 2021 at 23:00:54 UTC, Steven Schveighoffer wrote:
> On 2/3/21 4:00 PM, Q. Schroll wrote:
>> An easy way out would be giving the actual type of i"..." a property .interp or .__interp that (cf. tuple's expand) returns the interp sequence. That way, it can be tested in pragma(msg, i"...".__interp) while also being correct whenever used in a canonical form. __interp couldn't really be a library function. An alternative would be __traits(interp, i"...").
>
> Yeah, with your scheme, it would have to be a compiler directive somehow.

I don't think this is a problem.

>> The interpolation tuple really is an implementation detail that is relevant for an important, but still a rather small minority of functions.
>
> I'm not sure I see it that way. The interpolation tuple allows one to accept string/data lists without allocation or transformation. That is super-powerful and super useful.

My approach is not to take that power away. My concern is that it will hit you accidentally.

> I would agree that the major use case that most people will use is just to allocate a string.

I've thought about the formal semantics of my suggestion.

Formally, the compiler has to try the __interp and the string version. If both succeed (that's the relevant case) and instantiate THE SAME template, the string version (i.e. idup) will be used. Otherwise, the __interp is a better match and will be used.

This is precisely the case when there is a template that matches interp sequences, but not strings.

For supporting both, regular and interpolated strings, you need overloads, so that the interp sequence's match and the idup string's match is different.

There was a question how to do `execute` for an SQL builder. First, one overload would handle interp sequences. With that exact semantics explained above, you (Steven in particular) can use a contract, too:

    auto execute(InterpSeq...)(InterpSeq interpSeq)
        if (is(InterpSeq[0] == interp!str, string str))
    { /* handle interpolated string */ }

    auto execute(Args...)(string sqlTemplate, Args args)
    { /* handle regular sql template */ }

That way, execute("SELECT $1 FROM table", name) only matches the second and behaves as intended;
on the other hand, execute(i"SELECT ${name} FROM table") matches both:
The first via execute(interp!"SELECT "(), name, interp!" FROM table"())
and the second via execute(text("SELECT ", name, " FROM table")).
Because those are different templates, the first one is chosen that uses the interp sequence.
If I'm not mistaken, you can even mix them: execute(i"SELECT $1 FROM ${table}", column) can be made work by the first overload.

However, when you do tuple(i"I have ${nBananas} bananas", nBananas), both rewrites match the same template, so the rewrite tuple(text("I have ", nBananas, " bananas"), nBananas) is chosen.

I don't think it can be made better without hitting usability. With the proposal as-is, one could use `static if` and other reflection in a single variadic template. Requiring a specific overload to handle interpolated strings is probably the most maintainable form anyway, so I don't consider that a big ask.

With that semantics, free form variadic templates aren't instantiated in an unintended way. We cannot break all free form variadic templates and hope them to anticipate and handle interpolated strings. However, we can make writeln and friends use interpolated strings' powerful lowering and even if we happen to overlook a function that really should handle interpolated strings, it can be added later.

Everyone wins.
February 04, 2021
I feel it mentally easier to treat them as strings at compile time, too.

Can someone convince me why we are required to use tuples for compile time interpolations?

Is it owed to defer the process of interpolation after template expansion s.t. interpolation refers to the context inside the template instead of outside:

```
someTemplate!(i"${Hello}${World}")() //$Hello and $World will be substituted inside someTemplate
```

Can't we alternatively introduce another kind of string operator allowing us to defer string interpolation up to the considered place of substitution via the use of some closure?:

```
void someInterpolateFun(string function(string,string) intermediate)
{
    string s1=...
    string s2=...
    return intermediate(s1,s2)
}

string function(string Hello,string World) intermediate = di"${Hello}${World}" //di stand for delegate/defer + interpolate and creates a closure delegating the parameters into the interpolation.
someInterpolateFun(intermediate)
```

February 04, 2021
On Thursday, 4 February 2021 at 17:50:56 UTC, Meta wrote:
> I think it comes down to, when people (primarily and especially new users) see "", they expect a string.

It isn't "", it is i"". Javascript programmers seem to be able to figure out the concept that a different prefix may not yield a string. I'm sure D programmers can figure it out too.


Now my view btw is I'm down to two options:

1) You *always* get the naked tuple. It never converts to string, but of course, you can call a function to convert it to string.

int bar;
auto item = i"foo ${bar}";

typeof(item) == AliasSeq!(interp!"foo ", int);

string s = item; // compile error, type mismatch


This is what I already implemented so you can play with it immediately.

2) You *never* get the naked tuple. It is immediately passed to an object constructor. That object uses `alias toString this` to handle the string conversions and functions that want the whole thing just overload on that.

typeof(item) == InterpolatedObject!(interp!"foo ", int)

string s = item; // works under current rules because InterpolatedObject has alias toString this.


Adding this to the existing implementation is fairly simple since it is the same except a ctor wrapper and one more druntime type.



Pros of 1: maximum flexibility. It can do anything you can imagine by function calls, delegating 100% to user libraries. No unnecessary strings built.

Pros of 2: more traditional explanation, the implicit cast works under existing language rules.

Cons of 1: if you want a string, you *must* ask for one.

Cons of 2: sacrifices whatever potential there was in alias, ref. has quirks with inout, may not work in all cases with dip1000, dip 25, etc. It is basically a 90% solution. Also will require more library support (that toString has to call something, much of that is already in druntime, or it could import Phobos, so it isn't a big deal, but it does need to be there for the implicit conversion to work).



I've lost interest in anything else.