February 03, 2021
On Tuesday, 2 February 2021 at 19:38:08 UTC, 12345swordy wrote:
> On Tuesday, 2 February 2021 at 18:41:37 UTC, Imperatorn wrote:
>> On Tuesday, 2 February 2021 at 14:45:05 UTC, claptrap wrote:
>>> [...]
>>
>> Anyway you look at this it seems there's no clear concensus on how it should work.
>>
>> I really want this to pass, but not if there are many corner cases and/or special rules to follow.
>>
>> It should be as simple as possible while also being as powerful as possible. It's a tough balance.
>
> You don't need a clear consensus on this in order to pass, as you just need to convince two people here.
> -Alex

True, but I guess they read the comments.
February 03, 2021
On Friday, 29 January 2021 at 19:10:55 UTC, Steven Schveighoffer wrote:
> On 1/29/21 7:58 AM, Dukc wrote:
>> I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string.
>
> We debated whether one should be able to figure out the original interpolation string from the parameters. I don't think it's necessary, and adds unnecessary complexity.
>
> Just passing the expression data directly makes things easy to deal with instead of adding an extra type to deal with.
>
> If you can come up with a reasonable use case for differentiating, we can discuss.

Okay, finally did this:

```
void writeAtPositions(T...)(char[] wArea, T args)
{  import std;
   char[] outp = wArea;
   foreach(arg; args)
   static if(is(typeof(arg) : size_t) && !is(typeof(arg) : char))
   {  outp = wArea[arg .. $];
   }  else
   {  auto argStr= arg.text;
      outp[0 .. argStr.length] = argStr[];
      outp = outp.drop(argStr.length);
   }
}

void main()
{  import std.stdio;
   char[] text = "x=000,y=000,z=000".dup;
   text.writeAtPositions(2, '2', "55", 16, "6");
   text.writeln;
}
```

Consider passing ints in interpolated strings to `writeAtPositions`. Only change needed to work with my suggestion would be adding `!__traits(isSame, TemplateOf(typeof(arg), interp))` to the `static if`, (I assume interp!int would have `alias value this`). If the interpolated string `int`s are passed like any `int`s, one would probably have to manually cast the `int`s among interpolated strings to strings, to work with this function.
February 03, 2021
On 2/3/21 10:54 AM, Dukc wrote:
> On Friday, 29 January 2021 at 19:10:55 UTC, Steven Schveighoffer wrote:
>> On 1/29/21 7:58 AM, Dukc wrote:
>>> I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string.
>>
>> We debated whether one should be able to figure out the original interpolation string from the parameters. I don't think it's necessary, and adds unnecessary complexity.
>>
>> Just passing the expression data directly makes things easy to deal with instead of adding an extra type to deal with.
>>
>> If you can come up with a reasonable use case for differentiating, we can discuss.
> 
> Okay, finally did this:
> 
> ```
> void writeAtPositions(T...)(char[] wArea, T args)
> {  import std;
>     char[] outp = wArea;
>     foreach(arg; args)
>     static if(is(typeof(arg) : size_t) && !is(typeof(arg) : char))
>     {  outp = wArea[arg .. $];
>     }  else
>     {  auto argStr= arg.text;
>        outp[0 .. argStr.length] = argStr[];
>        outp = outp.drop(argStr.length);
>     }
> }
> 
> void main()
> {  import std.stdio;
>     char[] text = "x=000,y=000,z=000".dup;
>     text.writeAtPositions(2, '2', "55", 16, "6");
>     text.writeln;
> }
> ```
> 
> Consider passing ints in interpolated strings to `writeAtPositions`. Only change needed to work with my suggestion would be adding `!__traits(isSame, TemplateOf(typeof(arg), interp))` to the `static if`, (I assume interp!int would have `alias value this`). If the interpolated string `int`s are passed like any `int`s, one would probably have to manually cast the `int`s among interpolated strings to strings, to work with this function.

So the idea here is to use string interpolations to represent the string parts of the parameters.

To reimagine what you are thinking, something like:

int val = 255;

int otherval = 6;

text.writeAtPositions(2, i"${val}", 16, i"${otherval}");

So the parameters are going to be:

text, 2, interp!""(), val, interp!""(), 16, interp!""(), otherval, interp!""())

First, I'd suggest instrumenting the positions instead of the data, since that's the exceptional case:

struct Pos { size_t p; }
auto pos(size_t x) { return Pos(x); }

text.writeAtPositions(2.pos, i${val}", 16.pos, i"${otherval}");

And then you have a general mechanism that doesn't even need interpolation strings:

text.writeAtPositions(2.pos, val, 16.pos, otherval)

And now, you can use interpolation strings as the "things to write" without ambiguity. You can even use a single interpolation string (with the positions being interpolation parameters). I would say the non-interpolation form is better for when you are just passing in parameters, and the interpolation form is great when you want, well, interpolations.

I get the thrust of what you are saying. But technically, if you are having to reengineer the function to deal with interpolation strings, you should rethink how the API works.

FYI, something I have considered recently, because I don't *love* the mechanism for overloading is to put a non-template parameter first and last in the sequence. Which then makes it possible to discover where the sequences begin and end.

So instead of having to use template constraints for overloading:

void foo(T...)(T args) if (is(T[0] : interp!S, S))

you just use standard overloading:

void foo(T...)(__iStart, T args)

And as a bonus, you can discern interpolation parameters from regular ones (if desired).

-Steve
February 03, 2021
On Friday, 29 January 2021 at 12:58:32 UTC, Dukc wrote:
> On Thursday, 28 January 2021 at 14:58:36 UTC, Steven Schveighoffer at the feedback theard wrote:
>> On 1/28/21 3:35 AM, Dukc wrote:
>>> The DIP states that foo(i"a:${a}, ${b}.") is rewritten as `foo(Interp!"a:", a, Interp!", ", b, Interp!".")`. It think it's better to rewrite it as `foo(Interp!"a:", Interp!typeof(a)(a), Interp!", ", Interp!typeof(b)(b), Interp!".")`. That way, `foo` has easier time introspecting which came from the interpolated string.
>>
>> First, I don't think it's critical for overloading, and will simply add to the template bloat. What are you going to do differently with `a` than you would with `Interp!(typeof(a))(a)`?
>
> I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string. And I have a type that will implicitly convert to string if I want to do that - no need to call `to!string(a)` or `a.Interp!(typeof(a))` first.
>
>> The parameters are guaranteed to start and end with an InterpolationLiteral, so one can assume that non-literal arguments are interspersed inside the literal.
>
> It can be done, but it sounds more complex for the introspecting function. I'm not strict about this though, what the DIP now proposes be worth it to be able to pass `ref` parameters in interpolated strings.
>
>>
>>> The type of interpolated string literal is very special cased. [snip]
>>
>> I was fully aware that this would be the most controversial part. I feel like it will not be full of corner cases, but I'm not sure. Can you specify any?
>>
>> Consider a normal string literal can be used as a string, immutable(char)*, wstring, or dstring. I find it very similar to this feature, and I don't feel like there are a lot of corner cases there.
>
> A string literal is a string that is implicitly assignable to the other alternatives via value range propagation mechanics, or that's how I understand it at least.
>
> The compromise that the interpolated string would be an expanded tuple, that would be implicitly assignable to string via value range propagation mechanics, sounds acceptable. But it needs to be clear IMO what the primary type of an interpolated string is. If it is not an expanded tuple, what it is then? I mean that this must be guaranteed to pass IMO:

Sorry, I'm late to the game here. This reminds me of slices vs static arrays.
As a reminder, to a newcomer,
    auto xs = [ 1, 2, 3 ];
looks like it would infer int[3] as the type of xs. It is obviously the most descriptive type for the literal. Why would it infer int[] forgetting its compile-time known length and even do an allocation? That seems so much worse. Even typeof([1,2,3]) is int[] and not int[3]. We know why D does it the way it does and goes int[3] with no allocation only if requested explicitly. You can do that with a template with a flexible length like this:
    void takesStaticArray(size_t n)(int[n] staticArray);
Here, `n` can usually be inferred from the argument.

Interpolated strings could do the exact same thing:
1. make typeof(i"...") result to `string`.
2. make auto str = i"..." infer string (cf. typeof) and gc-allocate if necessary.
3. give i"..." a secondary type akin to [1,2,3] having int[3] as a secondary type.

If an interpolated string is bound to a parameter of that secondary type (`interp` in the DIP) it uses its secondary type (cf. calling takesStaticArray with [1,2,3]). In any other case, e.g. `auto` or otherwise generic template parameters will infer string.

Getting a string is probably what most users expect most of the time. Handling the secondary type must be explicit. It is almost an implementation detail that shouldn't be exposed to the user too easily.

(I'll post that to the feedback thread as well.)
February 03, 2021
On Wednesday, 3 February 2021 at 16:52:23 UTC, Q. Schroll wrote:
> On Friday, 29 January 2021 at 12:58:32 UTC, Dukc wrote:
>> [...]
>
> Sorry, I'm late to the game here. This reminds me of slices vs static arrays.
> As a reminder, to a newcomer,
>     auto xs = [ 1, 2, 3 ];
> looks like it would infer int[3] as the type of xs. It is obviously the most descriptive type for the literal. Why would it infer int[] forgetting its compile-time known length and even do an allocation? That seems so much worse. Even typeof([1,2,3]) is int[] and not int[3]. We know why D does it the way it does and goes int[3] with no allocation only if requested explicitly. You can do that with a template with a flexible length like this:
>     void takesStaticArray(size_t n)(int[n] staticArray);
> Here, `n` can usually be inferred from the argument.
>
> [...]

I also think that's reasonable, but Adam didn't seem to agree iirc (default to string)
February 03, 2021
On 2/3/21 11:52 AM, Q. Schroll wrote:
> On Friday, 29 January 2021 at 12:58:32 UTC, Dukc wrote:
>> On Thursday, 28 January 2021 at 14:58:36 UTC, Steven Schveighoffer at the feedback theard wrote:
>>> On 1/28/21 3:35 AM, Dukc wrote:
>>>> The DIP states that foo(i"a:${a}, ${b}.") is rewritten as `foo(Interp!"a:", a, Interp!", ", b, Interp!".")`. It think it's better to rewrite it as `foo(Interp!"a:", Interp!typeof(a)(a), Interp!", ", Interp!typeof(b)(b), Interp!".")`. That way, `foo` has easier time introspecting which came from the interpolated string.
>>>
>>> First, I don't think it's critical for overloading, and will simply add to the template bloat. What are you going to do differently with `a` than you would with `Interp!(typeof(a))(a)`?
>>
>> I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string. And I have a type that will implicitly convert to string if I want to do that - no need to call `to!string(a)` or `a.Interp!(typeof(a))` first.
>>
>>> The parameters are guaranteed to start and end with an InterpolationLiteral, so one can assume that non-literal arguments are interspersed inside the literal.
>>
>> It can be done, but it sounds more complex for the introspecting function. I'm not strict about this though, what the DIP now proposes be worth it to be able to pass `ref` parameters in interpolated strings.
>>
>>>
>>>> The type of interpolated string literal is very special cased. [snip]
>>>
>>> I was fully aware that this would be the most controversial part. I feel like it will not be full of corner cases, but I'm not sure. Can you specify any?
>>>
>>> Consider a normal string literal can be used as a string, immutable(char)*, wstring, or dstring. I find it very similar to this feature, and I don't feel like there are a lot of corner cases there.
>>
>> A string literal is a string that is implicitly assignable to the other alternatives via value range propagation mechanics, or that's how I understand it at least.
>>
>> The compromise that the interpolated string would be an expanded tuple, that would be implicitly assignable to string via value range propagation mechanics, sounds acceptable. But it needs to be clear IMO what the primary type of an interpolated string is. If it is not an expanded tuple, what it is then? I mean that this must be guaranteed to pass IMO:
> 
> Sorry, I'm late to the game here. This reminds me of slices vs static arrays.
> As a reminder, to a newcomer,
>      auto xs = [ 1, 2, 3 ];
> looks like it would infer int[3] as the type of xs. It is obviously the most descriptive type for the literal. Why would it infer int[] forgetting its compile-time known length and even do an allocation? That seems so much worse. Even typeof([1,2,3]) is int[] and not int[3]. We know why D does it the way it does and goes int[3] with no allocation only if requested explicitly. You can do that with a template with a flexible length like this:
>      void takesStaticArray(size_t n)(int[n] staticArray);
> Here, `n` can usually be inferred from the argument.
> 
> Interpolated strings could do the exact same thing:
> 1. make typeof(i"...") result to `string`.
> 2. make auto str = i"..." infer string (cf. typeof) and gc-allocate if necessary.
> 3. give i"..." a secondary type akin to [1,2,3] having int[3] as a secondary type.

I don't want to do it that way, because then the overload is not easy to ask for. I really really don't want the string form to be passed into a vararg template.

I've been having second thoughts about having auto x = i"..."; be a string, and typeof(i"...") be a string. See my later response here: https://forum.dlang.org/post/rv6jr4$uor$1@digitalmars.com

> If an interpolated string is bound to a parameter of that secondary type (`interp` in the DIP) it uses its secondary type (cf. calling takesStaticArray with [1,2,3]). In any other case, e.g. `auto` or otherwise generic template parameters will infer string.
> 
> Getting a string is probably what most users expect most of the time. Handling the secondary type must be explicit. It is almost an implementation detail that shouldn't be exposed to the user too easily.

I'm not sure how this could be possible. You can't say T... and have it match the tuple over the string, unless the primary type is the tuple.

But if you have ideas, surely I'd prefer it to be a string first in most cases!

-Steve
February 03, 2021
On Wednesday, 3 February 2021 at 16:38:15 UTC, Steven Schveighoffer wrote:
> First, I'd suggest instrumenting the positions instead of the data, since that's the exceptional case:
>
> [snip]
>
> And now, you can use interpolation strings as the "things to write" without ambiguity. You can even use a single interpolation string (with the positions being interpolation parameters). I would say the non-interpolation form is better for when you are just passing in parameters, and the interpolation form is great when you want, well, interpolations.
>
> I get the thrust of what you are saying. But technically, if you are having to reengineer the function to deal with interpolation strings, you should rethink how the API works.

Good, obviously you got the point and your answer sounds convincing enough that I don't think there's need for me to argue about that one any further.


February 03, 2021
On Wednesday, 3 February 2021 at 17:40:40 UTC, Steven Schveighoffer wrote:
> On 2/3/21 11:52 AM, Q. Schroll wrote:
>> On Friday, 29 January 2021 at 12:58:32 UTC, Dukc wrote:
>>> On Thursday, 28 January 2021 at 14:58:36 UTC, Steven Schveighoffer at the feedback theard wrote:
>>>> On 1/28/21 3:35 AM, Dukc wrote:
>>>>> The DIP states that foo(i"a:${a}, ${b}.") is rewritten as `foo(Interp!"a:", a, Interp!", ", b, Interp!".")`. It think it's better to rewrite it as `foo(Interp!"a:", Interp!typeof(a)(a), Interp!", ", Interp!typeof(b)(b), Interp!".")`. That way, `foo` has easier time introspecting which came from the interpolated string.
>>>>
>>>> First, I don't think it's critical for overloading, and will simply add to the template bloat. What are you going to do differently with `a` than you would with `Interp!(typeof(a))(a)`?
>>>
>>> I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string. And I have a type that will implicitly convert to string if I want to do that - no need to call `to!string(a)` or `a.Interp!(typeof(a))` first.
>>>
>>>> The parameters are guaranteed to start and end with an InterpolationLiteral, so one can assume that non-literal arguments are interspersed inside the literal.
>>>
>>> It can be done, but it sounds more complex for the introspecting function. I'm not strict about this though, what the DIP now proposes be worth it to be able to pass `ref` parameters in interpolated strings.
>>>
>>>>
>>>>> The type of interpolated string literal is very special cased. [snip]
>>>>
>>>> I was fully aware that this would be the most controversial part. I feel like it will not be full of corner cases, but I'm not sure. Can you specify any?
>>>>
>>>> Consider a normal string literal can be used as a string, immutable(char)*, wstring, or dstring. I find it very similar to this feature, and I don't feel like there are a lot of corner cases there.
>>>
>>> A string literal is a string that is implicitly assignable to the other alternatives via value range propagation mechanics, or that's how I understand it at least.
>>>
>>> The compromise that the interpolated string would be an expanded tuple, that would be implicitly assignable to string via value range propagation mechanics, sounds acceptable. But it needs to be clear IMO what the primary type of an interpolated string is. If it is not an expanded tuple, what it is then? I mean that this must be guaranteed to pass IMO:
>> 
>> Sorry, I'm late to the game here. This reminds me of slices vs static arrays.
>> As a reminder, to a newcomer,
>>      auto xs = [ 1, 2, 3 ];
>> looks like it would infer int[3] as the type of xs. It is obviously the most descriptive type for the literal. Why would it infer int[] forgetting its compile-time known length and even do an allocation? That seems so much worse. Even typeof([1,2,3]) is int[] and not int[3]. We know why D does it the way it does and goes int[3] with no allocation only if requested explicitly. You can do that with a template with a flexible length like this:
>>      void takesStaticArray(size_t n)(int[n] staticArray);
>> Here, `n` can usually be inferred from the argument.
>> 
>> Interpolated strings could do the exact same thing:
>> 1. make typeof(i"...") result to `string`.
>> 2. make auto str = i"..." infer string (cf. typeof) and gc-allocate if necessary.
>> 3. give i"..." a secondary type akin to [1,2,3] having int[3] as a secondary type.
>
> I don't want to do it that way, because then the overload is not easy to ask for. I really really don't want the string form to be passed into a vararg template.

I think I understand you. I want the best of all worlds, too. Maybe I'm just not seeing it. As of now, I'm really convinced that, while it would be nice to have, it just would break too much intuition. Using something as simple and common as i"..." correctly MUST be trivial. One shouldn't have to look up how it works.

> I've been having second thoughts about having auto x = i"..."; be a string, and typeof(i"...") be a string. See my later response here: https://forum.dlang.org/post/rv6jr4$uor$1@digitalmars.com

I didn't think too much about typeof(i"..."), but I agree that it being string but behaving differently form a plain string may be odd.

>> If an interpolated string is bound to a parameter of that secondary type (`interp` in the DIP) it uses its secondary type (cf. calling takesStaticArray with [1,2,3]). In any other case, e.g. `auto` or otherwise generic template parameters will infer string.
>> 
>> Getting a string is probably what most users expect most of the time. Handling the secondary type must be explicit. It is almost an implementation detail that shouldn't be exposed to the user too easily.
>
> I'm not sure how this could be possible. You can't say T... and have it match the tuple over the string, unless the primary type is the tuple.

I'm not completely sure what that paragraph means.

> But if you have ideas, surely I'd prefer it to be a string first in most cases!

I'd say the best idea is to make i"..." decay into string unless forced to be interp!".." (or really being a better match, really think about it like binding [1,2,3] to int[3]). 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"...").

The interpolation tuple really is an implementation detail that is relevant for an important, but still a rather small minority of functions.
February 03, 2021
On Monday, 1 February 2021 at 16:10:46 UTC, Adam D. Ruppe wrote:
> [complaining about .stringof]
>
> So why the heck is that built into the language?!?

It makes for nice error messages, tho. I don't care much about the exact string there, only that it is recognizable (which for (++i).stringof it clearly isn't).
February 03, 2021
On Wednesday, 3 February 2021 at 21:13:44 UTC, Q. Schroll wrote:
> It makes for nice error messages, tho. I don't care much about the exact string there, only that it is recognizable (which for (++i).stringof it clearly isn't).

stringof for error messages is fine. What perplexes me is that mixin(x) will translate to mixin(x.stringof) automatically even though that's a mistake more often than not.