December 06, 2018
On Thursday, 6 December 2018 at 22:24:50 UTC, Steven Schveighoffer wrote:
> `text(...)` is the way to go. Or rather `i"a is $a".text`;
>
> -Steve

I just tried this out (https://run.dlang.io/is/RmRBZ8), but I don't know why it doesn't work.

This:
```
import std.stdio: writeln;
import std.conv: text;
import std.typecons: tuple;

void main() {
	string name = "Jeff";
	writeln(tuple("my name is ", name).text);
}
```
Prints "Tuple!(string, string)("my name is ", "Jeff")", not "My my name is Jeff".
December 06, 2018
On Thursday, 6 December 2018 at 22:54:54 UTC, o wrote:
> Prints "Tuple!(string, string)("my name is ", "Jeff")", not "My my name is Jeff".

You're converting the tuple object to a string and passing that string as a single argument to writeln. Try:
```
writeln(tuple("my name is ", name).expand);
```
December 06, 2018
On Thursday, 6 December 2018 at 22:54:54 UTC, o wrote:
> import std.typecons: tuple;

That's a library tuple, which is just syntax salt over a struct.

When we say tuple here, we are actually referring to a compiler tuple, which is more like AliasSeq from std.meta - a magic type that represents an argument list.
December 06, 2018
On Thursday, 6 December 2018 at 20:40:02 UTC, Paul Backus wrote:
> I'm not convinced that libraries *should* have access to that info.


Yeah. I can imagine some cool stuff I'd do to play with it... but it isn't really important.


Let's go back to the simple proposal, turning it into the tuple like Jonathan Marler's PR.
December 06, 2018
On Thursday, 6 December 2018 at 22:53:26 UTC, Paul Backus wrote:
> On Thursday, 6 December 2018 at 22:34:14 UTC, o wrote:
>> Just think how much nicer this is (lowering to a string):
>> `foo(i"My name is $name");`
>>
>> Compared to this (lowering to a tuple, requiring a conversion to a string):
>> ```
>> import std.conv: text;
>> foo(text(i"My name is $name"));
>> ```
>
> You can write `foo` in such a way that the user never has to manually insert a call to `text`:
>
> void foo(string s)
> {
>     // do whatever
> }
>
> void foo(Args...)(Args args)
> {
>     import std.conv: text;
>     foo(text(args));
> }
>
> In fact, you can even encapsulate this pattern in a mixin:
>
> template interpArgs(alias fun)
> {
>     import std.format: format;
>
>     enum interpArgs = q{
>         auto %1$s(Args...)(Args args)
>         {
>             import std.conv: text;
>             return %1$s(text(args));
>         }
>     }.format(__traits(identifier, fun));
> }
>
> mixin(interpArgs!foo);

I think that this just further proves my point. In order use an interpolated string as a real string, you ended up going back down this rabbit-hole of mixins/libraries/templetes. There would be no need for this if `foo(i"a is $a)` was the same as `foo("a is 12")`.


December 06, 2018
On Thursday, 6 December 2018 at 22:52:28 UTC, Dennis wrote:
> On Thursday, 6 December 2018 at 22:34:14 UTC, o wrote:
>> Just think how much nicer this is (lowering to a string):
>> `foo(i"My name is $name");`
>>
>> Compared to this (lowering to a tuple, requiring a conversion to a string):
>> ```
>> import std.conv: text;
>> foo(text(i"My name is $name"));
>> ```
>
> With UFCS it looks a lot nicer already:
> ```
> foo(i"My name is $name".text);
> ```
> How often do you actually need to pass an interpolated string to a function in string form? That's not a rethorical question, it is important to consider. If 90% of string interpolation happens in writeln / logging then it's not worth forcing the use of phobos + GC + eager toString and concatenation just to make the remaining 10% of cases a bit easier.

"forcing the use of phobos" does not only apply to lower-to-string interpolation. Tuple and 'text()' functionality all comes from phobos in the first place.

> If 90% of string interpolation happens in writeln / logging then it's not worth forcing the use of phobos + GC + eager toString and concatenation just to make the remaining 10% of cases a bit easier.

So lets say that tuple-interpolation covers 90% of the use cases. What about the other 10%? If we used 2 different string prefixes, ie `i"..."` for lower-to-string interpolation and `t"..."` for lower-to-tuple, then 100% of the use cases are covered and everyone is happy.
December 06, 2018
On Thursday, 6 December 2018 at 23:10:48 UTC, o wrote:
> "forcing the use of phobos" does not only apply to lower-to-string interpolation. Tuple and 'text()' functionality all comes from phobos in the first place.

But `text` is something you choose to import and call yourself. You are free to implement your own text function for a BetterC / WebAssembly environment. And, as Adam clarified, it's not lowered to a Phobos Tuple but a 'compiler tuple'.

> If we used 2 different string prefixes, ie `i"..."` for lower-to-string interpolation and `t"..."` for lower-to-tuple, then 100% of the use cases are covered and everyone is happy.

I think we can be happy if we can make a convincing case that one of these is worth the extra complexity. Trying to get in two new string types with huge overlap is probably not worth it. It also raises the question 'when to use which' which will not be obvious to many users.
December 06, 2018
On 12/6/18 6:05 PM, o wrote:
> On Thursday, 6 December 2018 at 22:53:26 UTC, Paul Backus wrote:
>> On Thursday, 6 December 2018 at 22:34:14 UTC, o wrote:
>>> Just think how much nicer this is (lowering to a string):
>>> `foo(i"My name is $name");`
>>>
>>> Compared to this (lowering to a tuple, requiring a conversion to a string):
>>> ```
>>> import std.conv: text;
>>> foo(text(i"My name is $name"));
>>> ```
>>
>> You can write `foo` in such a way that the user never has to manually insert a call to `text`:
>>
>> void foo(string s)
>> {
>>     // do whatever
>> }
>>
>> void foo(Args...)(Args args)
>> {
>>     import std.conv: text;
>>     foo(text(args));
>> }
>>
>> In fact, you can even encapsulate this pattern in a mixin:
>>
>> template interpArgs(alias fun)
>> {
>>     import std.format: format;
>>
>>     enum interpArgs = q{
>>         auto %1$s(Args...)(Args args)
>>         {
>>             import std.conv: text;
>>             return %1$s(text(args));
>>         }
>>     }.format(__traits(identifier, fun));
>> }
>>
>> mixin(interpArgs!foo);
> 
> I think that this just further proves my point. In order use an interpolated string as a real string, you ended up going back down this rabbit-hole of mixins/libraries/templetes. There would be no need for this if `foo(i"a is $a)` was the same as `foo("a is 12")`.

But it's not. It's foo("a is " ~ a.to!string);

I'd rather the allocation(s) be explicit.

foo(i"a is $a".text)

looks good to me, and solves the problem.

-Steve
December 07, 2018
On Thursday, 6 December 2018 at 22:04:13 UTC, Steven Schveighoffer wrote:

> It's really not what's enabled (clearly, things can still be used without string interpolation, and we can use mixins to simulate close to what we would want).
>
> It's how one writes and reads code. The efficiency and elegance is not to be ignored.

[..]

Of course, you are right, and that's what I was trying to say.  "What language feature do we need, or limitation removed, that gets us what we want:  the ability to implement interpolated strings and potentially other features in the library without too much compromise on the efficiency, elegance, and expressiveness you would get if you implemented it in the language, and without turning D into a DSL".  That probably doesn't do it justice either, but I hope you get the idea and we're on more-or-less the same page.

Jacob mentioned AST macros (I knew that was coming, and even had a note in my first draft say "Sorry, Jacob, except for AST macros", but I deleted it :D ).  But Jacob is on to something.  We already pass around symbols with `alias`.  Can that be extended somehow to pass around expressions too (i.e. expressions as rvalues)?  That would probably help address the issue of printing assert expressions as well (https://github.com/dlang/dmd/pull/8517)

Zodian mentioned eponymous mixin template at https://forum.dlang.org/post/tnebtoplmhtpabnlvydw@forum.dlang.org

H. S. Teoh mentioned using mixins as expressions at https://forum.dlang.org/post/mailman.5553.1544132869.29801.digitalmars-d@puremagic.com

As for myself, I don't see much value in forcing users to add `mixin` at the site of a mixin template instantiation, but there's a lot I don't see, so I may just be missing something.
```
import std.stdio;

mixin template Foo(T)
{
    T x = 5;
}

extern(C) void main()
{
    // Why do we need `mixin` here?
    // Why can't we just write `Foo!int;`?
    // It's already yelling at us with a ! operator.
    mixin Foo!int;
    writeln(x);
}
```

Simen Kjærås mentioned a few things when we were discussing ways to implement properties in the library at https://forum.dlang.org/post/jqcqsriizuewdqotbnop@forum.dlang.org  There might be some feature to be added, or limitation removed, that will permit help both the string interpolation case and the properties case.

I know I'm just thinking out loud and not contributing much, but I can't help but feel that there's something common in all these ideas that just hasn't been identified yet.  And no, Jacob, not AST macros (Actually, that would probably do it, but I think we've already been there) :D

Mike

December 07, 2018
On Thursday, 6 December 2018 at 23:02:43 UTC, Adam D. Ruppe wrote:
> On Thursday, 6 December 2018 at 22:54:54 UTC, o wrote:
>> import std.typecons: tuple;
>
> That's a library tuple, which is just syntax salt over a struct.
>
> When we say tuple here, we are actually referring to a compiler tuple, which is more like AliasSeq from std.meta - a magic type that represents an argument list.

Thanks for the clarification. I know realize the real potential of using tuples here instead of toString-ing :-).

+1 for lowering to tuples.