February 03, 2021
On Wednesday, 3 February 2021 at 20:12:24 UTC, Adam D. Ruppe wrote:
> On Wednesday, 3 February 2021 at 20:02:57 UTC, Daniel N wrote:
>> Wouldn't this lowering be both simpler and more more efficient?
>
> Your change wouldn't expose the string for compile time processing.
>
> That's the real benefit of interp!"string" - it is available for CTFE rewriting by the function.

Guess it's not allowed to keep discussing in the feedback thread so I have to copy it here.

alias I(T...) = T;
alias X(T...) = I!(interp!(T[0]), interp!(T[1]));

Sorry for being daft, I simply don't get it... there's no issue with applying map to a tuple of string literals...
X!("one","two")

Maybe you have to jump through some extra hoops, but in the common case, is more optimized if there are no superfluous template instances for every little string...

/Daniel
February 03, 2021
On Wednesday, 3 February 2021 at 22:07:47 UTC, Daniel N wrote:
> On Wednesday, 3 February 2021 at 20:12:24 UTC, Adam D. Ruppe wrote:
>> On Wednesday, 3 February 2021 at 20:02:57 UTC, Daniel N wrote:
>>> Wouldn't this lowering be both simpler and more more efficient?
>>
>> Your change wouldn't expose the string for compile' time processing.
>>
>> That's the real benefit of interp!"string" - it is available for CTFE rewriting by the function.
>
> Guess it's not allowed to keep discussing in the feedback thread so I have to copy it here.
>
> alias I(T...) = T;
> alias X(T...) = I!(interp!(T[0]), interp!(T[1]));
>
> Sorry for being daft, I simply don't get it... there's no issue with applying map to a tuple of string literals...
> X!("one","two")

The idea is that, even when `interp!"whatever"` is passed as a *runtime* argument to a template function, that function can access the string "whatever" at compile time, because it's part of the argument's *type* rather than its *value*.

void fun(Args...)(Args args)
{
    static foreach (arg; args) {
        static if (is(arg == interp!s, string s)) {
            // Allowed to do this because `s` is known at compile time
            pragma(msg, s);
        }
    }
}

// prints "Hello " at compile-time
fun(i"Hello ${name}");
February 03, 2021
On 2/3/21 4:00 PM, Q. Schroll wrote:
> 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 get that position totally, and it's mostly where I'm coming from too. A string interpolation is meant to be a string first, and an expanded tuple if supported. I would be happy with a system where it's easy to specify that a function can accept interpolation tuples instead of strings as a variadic parameter.

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

I mean, let's say we wanted to make a function that doesn't just accept an interpolation tuple, it can accept multiple tuples. How do you write the signature for that? Or maybe you have optional parameters that look awkward inside the interpolation sequence? Once you get into variadic parameters, you have no power (except via template constraints) to say the interpolation tuple form should be used.

Maybe in practice this isn't needed, but I am uneasy with no way to express that API.

Note that I use this capability to explain why we don't need concatenation, because 2 tuples separated by a comma are still a tuple.

Also, the fact that the tuple matches "Best effort" functions such as writeln and text (and probably a host of others, such as logging) I found to be very pleasing. Basically any place that accepts an untyped list of things to stringify.

As pointed out, there are also other cases where an untyped list of things is used for non-stringy things (such as std.typecons.tuple). It might end up being, you just have to live with that use case.

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

I read your post on the feedback thread, and it's definitely a different take than I had considered. Perhaps there is a way to do it, but for sure your mechanism of taking the string parameter of the first interp struct as a template parameter is a better mechanism than what I had thought of with template constraints.

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

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

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

-Steve
February 04, 2021
On Wednesday, 3 February 2021 at 22:15:45 UTC, Paul Backus wrote:
> On Wednesday, 3 February 2021 at 22:07:47 UTC, Daniel N wrote:
>> On Wednesday, 3 February 2021 at 20:12:24 UTC, Adam D. Ruppe wrote:
>>> On Wednesday, 3 February 2021 at 20:02:57 UTC, Daniel N wrote:
>>>> Wouldn't this lowering be both simpler and more more efficient?
>
> The idea is that, even when `interp!"whatever"` is passed as a *runtime* argument to a template function, that function can access the string "whatever" at compile time, because it's part of the argument's *type* rather than its *value*.
>
> void fun(Args...)(Args args)
> {
>     static foreach (arg; args) {
>         static if (is(arg == interp!s, string s)) {
>             // Allowed to do this because `s` is known at compile time
>             pragma(msg, s);
>         }
>     }
> }
>
> // prints "Hello " at compile-time
> fun(i"Hello ${name}");

Thank you! It is very impressive, brilliant and clever, yet I don't think it's the right way.

With a plain tuple it is possible to create a wrapper which generates DIP1036 style lowering (or simply work directly in the template with the value params).

template fun(T...) {
    void fun() {
        fun(interp!(T[0])(), interp!(T[1])());
    }
}

In order of greatness:
1) Opt-In  Bloat
2) Out-Out Bloat
3) Unavoidable Bloat <- Current DIP1036

Yes, as a library author you are forced to use value templates... but it still works, no real expressive power is lost.

Shouldn't we optimize for the common-case? (writeln and mixin)

Strings are ubiquitous, lowering to a plain tuple is the least possible bloat and also most powerful (support for ref etc), but with my proposed simplification you are now free to opt-in to more advanced meta programming _when you need it_, pay as you go!


February 04, 2021
On Wednesday, 27 January 2021 at 10:33:08 UTC, Mike Parker wrote:
> This is the discussion thread for the second round of Community Review of DIP 1036, "String Interpolation Tuple Literals":

The whole "${}" thing is too verbose, it reminds me of the JavaScript variant which is basically no easier to read than concatenation.  Python got it mostly right...

But I think D should reject superfluous features like this that has very limited benefits. The compiler should be made simpler, and deal with things like memory and type system before more code is added.


February 04, 2021
On Thursday, 4 February 2021 at 08:10:57 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 27 January 2021 at 10:33:08 UTC, Mike Parker wrote:
>> This is the discussion thread for the second round of Community Review of DIP 1036, "String Interpolation Tuple Literals":
>
> The whole "${}" thing is too verbose, it reminds me of the JavaScript variant which is basically no easier to read than concatenation.  Python got it mostly right...
>
> But I think D should reject superfluous features like this that has very limited benefits. The compiler should be made simpler, and deal with things like memory and type system before more code is added.

In Python, there is just the dollar sign less...

--


The DIP does not mention anything about wstring and dstring.

```
double price = 1.05;
auto a = i"Une baguette coûte ${price}€"w;
```

Should works too.
February 04, 2021
On Thursday, 4 February 2021 at 08:10:57 UTC, Ola Fosheim Grøstad wrote:

> The whole "${}" thing is too verbose, it reminds me of the JavaScript variant which is basically no easier to read than concatenation.  Python got it mostly right...
>

In my opinion,

  `${a} plus ${b} is ${a+b}`

is definitely easier to read than

  "" + a + " plus " + b + " is " + (a+b)


Dart
  "${a} plus ${b} is ${a+b}"
  or
  "$a plus $b is ${a+b}"

Kotlin
  "${a} plus ${b} is ${a+b}"
  or
  "$a plus $b is ${a+b}"

Javascript/Typescript
  `${a} plus ${b} is ${a+b}`


Python
  template = Template('$a plus $b is $c)
  print( template.substitute(a=a, b=b, c=a+b) );

  or

  print( Template('$a plus $b is $c).substitute(a=a, b=b, c=a+b) );


That without taking into account that python solution is a "run time" template compiler while Dart/typescript/... is a compile time type safe in place string expression.
February 04, 2021
On Thursday, 4 February 2021 at 08:10:57 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 27 January 2021 at 10:33:08 UTC, Mike Parker wrote:
>> This is the discussion thread for the second round of Community Review of DIP 1036, "String Interpolation Tuple Literals":
>
> The whole "${}" thing is too verbose, it reminds me of the JavaScript variant which is basically no easier to read than concatenation.  Python got it mostly right...

Isn't the {} thing so you can do..

i"i ate ${apples + bananas} of my 5 a day"

Maybe it could allow

i"i ate $apples apples today"

too?
February 04, 2021
On Thursday, 4 February 2021 at 11:00:24 UTC, claptrap wrote:
> On Thursday, 4 February 2021 at 08:10:57 UTC, Ola Fosheim Grøstad wrote:
>> On Wednesday, 27 January 2021 at 10:33:08 UTC, Mike Parker wrote:
>>> This is the discussion thread for the second round of Community Review of DIP 1036, "String Interpolation Tuple Literals":
>>
>> The whole "${}" thing is too verbose, it reminds me of the JavaScript variant which is basically no easier to read than concatenation.  Python got it mostly right...
>
> Isn't the {} thing so you can do..
>
> i"i ate ${apples + bananas} of my 5 a day"
>
> Maybe it could allow
>
> i"i ate $apples apples today"
>
> too?

Or get rid of $ like in C# and have

i"I ate {nrOfApples} apples today" and
i"I ate {nrOfApples + nrOfOranges} fruits today"

Clean imo
February 04, 2021
On Thursday, 4 February 2021 at 09:22:38 UTC, Luhrel wrote:
> The DIP does not mention anything about wstring and dstring.

That's what StringPosfix is in the grammar:

+InterpolatedDoubleQuotedString:
+    i" InterpolatedDoubleQuotedCharacters " StringPostfix[opt]

From the text: "the result of the idup function will be a string, wstring, or dstring depending on the type of the literal."


Already in my poc too: https://github.com/adamdruppe/dmd/tree/string_interp