December 07, 2018
Let me clarify my ideas:

The goal is to be able to implement string interpolation _with nice syntax_ as library code.
The code we are aiming for is something like this:
```
int a = 5;
int b = 42;
string b = interp!"a is $(a) and b is $(b)";
```

What is possible today?
````
mixin template interp(string S) {
	enum INTERP_RESULT = tuple("a is ", a, " and b is ", b); // replace with code that generates tuple at compile time, based on S and captured scope variables
}
void main() {
	int a = 5;
	int b = 42;
	mixin interp!"a is $(a) and b is $(b)";
	writeln(INTERP_RESULT);
}
```

So what do we need to support our goal?
a) mixin as expressions
````
mixin template interp(string S) {
	enum INTERP_RESULT = tuple("a is ", a, " and b is ", b); // replace with code that generates tuple at compile time, based on S and captured scope variables
}
void main() {
	int a = 5;
	int b = 42;
	writeln(mixin interp!"a is $(a) and b is $(b)".INTERP_RESULT);
}
```

b) a way to refer to a symbol inside that mixin automatically (Eponymous mixin templates)
   i'm using 'mixin interp =' as a way to say this is an Eponymous mixin template. But maybe it is enough to have 'alias interp =' or 'enum interp ='.
   like in normal templates: template ASDF(T) { enum ASDF = ...; }
````
mixin template interp(string S) {
	mixin interp = tuple("a is ", a, " and b is ", b); // replace with code that generates tuple at compile time, based on S and captured scope variables
}
void main() {
	int a = 5;
	int b = 42;
	writeln(mixin interp!"a is $(a) and b is $(b)");
}
```

c) (optional) a way to not require 'mixin' to be repeated at the call site.
````
mixin template interp(string S) {
	mixin interp = tuple("a is ", a, " and b is ", b); // replace with code that generates tuple at compile time, based on S and captured scope variables
}
void main() {
	int a = 5;
	int b = 42;
	writeln(interp!"a is $(a) and b is $(b)");
}
```

A problem that might exist is that you can define multiple things inside a mixin template.
but for it to be useable as an mixin expression only one does make sense, so my idea was to only allow Eponymous mixin templates to be used as mixin expressions.
Also i might be missing something totally obvious and this does not work at all. But i think we should try to explore ways to make implementation in library code possible _and_ pretty, before we implement string interpolation in the compiler.

So looking at this enhancement request (https://issues.dlang.org/show_bug.cgi?id=18586), i'd change this code:
```
mixin template fun(string body) {
    mixin("auto fun() { "~body~" }");
}
```
to this:
```
mixin template fun(string body) {
    mixin("mixin fun = (){ return "~body~"; }");
//or maybe: mixin("alias fun = (){ return "~body~"; }");
}
```

December 07, 2018
On Friday, 7 December 2018 at 14:40:05 UTC, Jonathan Marler wrote:
>> Perhaps what's needed is a different mixin operator that is not verbose and significantly distinguishes it from the template instantiation operator.
>>
>> mixin i!"...";  // instead of this...  (too verbose)
>> i!"...";        // or this... (looks too much like a template instantiation)
>> i#"...";        // we create a new mixin operator `#`
>>
>> Mike
>
> An interesting idea.
>
> Maybe id#(...) lowers to mixin(id(...)) Or something.

Congratulations, you've invented Lisp reader macros. :)

https://lisper.in/reader-macros
December 07, 2018
On 12/6/18 6:02 PM, 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.

Syntax salt. Love that.

-Steve
December 07, 2018
On Thursday, 6 December 2018 at 00:10:56 UTC, o wrote:
> I really wish that D had string interpolation, and I can see that a ton of other people also do. String interpolation has been brought up before, but it has been ignored/put down every time. Jonathan Marler even created a Pull Request (https://github.com/dlang/dmd/pull/7988) to add this feature, but he ended up closing it because it was getting nowhere. The excuse that keeps being given for not adding this feature is something along the lines of "There are ways to work around this, so it is unnecessary to add it". If this is the case, then why do people keep on requesting it again and again? Here are just some cases of other people asking for string interpolation:
>
> - https://forum.dlang.org/thread/c2q7dt$67t$1@digitaldaemon.com
> - https://forum.dlang.org/thread/qpuxtedsiowayrhgyell@forum.dlang.org
> - https://forum.dlang.org/thread/ncwpezwlgeajdrigegee@forum.dlang.org
> And there are 2 closed PRs for this:
> - https://github.com/dlang/dmd/pull/6703
> - https://github.com/dlang/dmd/pull/7988
>
> In my opinion, having a sizable amount of people request a feature (about 100 out of 280 as of The State of D Survey [https://dlang.typeform.com/report/H1GTak/PY9NhHkcBFG0t6ig]) is a good enough to realize that maybe we should start thinking of adding it to the language.
>
> I understand that this may involve a DIP process, which takes time. Therefore I am willing to offer up $100 to someone if they are willing to go through the DIP process for string interpolation.
>
> And please don't mention Phobos' sorry excuse for string interpolation:
> "a is: %s, b is: %s, sum is %s.".format(a, b, a + b)
> Is no better than
> "a is: "~a.to!string~"b is: "~b.to!string~" and the sum is: "~(a+b).to!string~"."

It's better, the latter is much worse. Longer, harder, to read, uglier. AFAIC `.to!string` is an anti-pattern.

> This is just so much more readable, and maintainable:
> "a is: ${a}, b is: ${b}, and the sum is: ${a+b}."

So is this:

text("a is", a, ", b is ", b, " and the sum is: ", a + b);

Which works today.

The only use I'd have for string interpolation is when generating code (as a string) that spans multiple lines. Before this thread I wasn't aware that scriptlike had string interpolation. Now that I know it does, I don't know what I'd need "proper" (i.e. language assisted) SI.

Every language change is a cost, and therefore should justify its inclusion. I personally don't think that it is in this case just to make a niche use case slightly easier, and this coming from someone from that niche! From now on it's:

const code = mixin(interp!q{
    // stuff that I used to write with many %s and it was hard to match them up
});
December 07, 2018
On Friday, 7 December 2018 at 17:11:35 UTC, Atila Neves wrote:
> So is this:
>
> text("a is", a, ", b is ", b, " and the sum is: ", a + b);
>
> Which works today.

Except you made a mistake: there is no whitespace between "a is" and a, so it will be glued together. Kind of hard to notice without string interpolation. ;)

December 07, 2018
On Friday, 7 December 2018 at 17:11:35 UTC, Atila Neves wrote:
> The only use I'd have for string interpolation is when generating code (as a string) that spans multiple lines. Before this thread I wasn't aware that scriptlike had string interpolation. Now that I know it does, I don't know what I'd need "proper" (i.e. language assisted) SI.
>
> Every language change is a cost, and therefore should justify its inclusion. I personally don't think that it is in this case just to make a niche use case slightly easier, and this coming from someone from that niche! From now on it's:
>
> const code = mixin(interp!q{
>     // stuff that I used to write with many %s and it was hard to match them up
> });

Your reasoning is sound but we differ in opinion here.  In my opinion the cost is low.  The implementation is very isolated.  It's a simple change to the lexer and a new function call in parse stage that returns a tuple, that's it. You also say the usefullness is "niche" but I would disagree.  If it's implemented in the language then it could potentially replace the majority of calls to writeln/writefln.  It's more efficient than writefln and easier to read/maintain than writeln.  You yourself made a mistake in your own example because writeln is so clumsy to use.  That's how I would use it anyway, I can't speak for everyone else but I think many people see this benefit as well.

December 07, 2018
On Fri, Dec 07, 2018 at 05:11:35PM +0000, Atila Neves via Digitalmars-d wrote: [...]
> Every language change is a cost, and therefore should justify its inclusion.  I personally don't think that it is in this case just to make a niche use case slightly easier, and this coming from someone from that niche! From now on it's:
> 
> const code = mixin(interp!q{
>     // stuff that I used to write with many %s and it was hard to match them
> up
> });

One thing I like about format() that string interpolation doesn't (easily) give, is the ability to specify additional formatting parameters, e.g., %6.2f, or %04X. You'd have to basically "uglify" string interpolation in much the same ways in order to get equivalent functionality, e.g., (hypothetical syntax) "Price is ${6.2:price}.".

The one thing missing from format(), as you point out above, is the lack of named arguments for long format strings.  This could be fixed by extending format() with named placeholders, so that you can do something like:

	// Hypothetical syntax
	format(q"CODETEMPLATE
		void %{funcName}s(%{funcParams}(%s, %)) {
			%{boilerPlate}s
			for (i; 0 .. %{numIter}d)
			{
				%{loopBody}s
			}
		}
	CODETEMPLATE", NamedArgs([
		"funcName", "myFunc",
		"funcParams", ["int x", "float y", "string z"],
		"boilerPlate", generateBoilerplate(),
		"loopBody", generateFuncBody(),
		"numIter", 255
	]));

Basically, instead of sequential arguments (or numerically-indexed arguments like $#1), format takes what amounts to a polymorphic associative array from which it can look up named arguments. This gets rid of the "too many %s I can't figure out which argument is which" problem, plus it makes your format string more readable and maintainable. It allows the same argument to be referenced multiple times, and arguments don't have to appear in the order they appear in the format string (important for i18n uses).

Note that NamedArgs can be dispensed with if the language supported
named arguments (like in Python, `myfunc(width = 123, height = 234)`).


T

-- 
I am Ohm of Borg. Resistance is voltage over current.
December 07, 2018
On 12/7/18 11:26 AM, H. S. Teoh wrote:
> One thing I like about format() that string interpolation doesn't
> (easily) give, is the ability to specify additional formatting
> parameters, e.g., %6.2f, or %04X. You'd have to basically "uglify"
> string interpolation in much the same ways in order to get equivalent
> functionality, e.g., (hypothetical syntax) "Price is ${6.2:price}.".

This seems like it would fit with the existing language:

"Price is ${6.2.price}."

SomeFormattingRange price(double d) {..}
December 07, 2018
On Friday, 7 December 2018 at 18:26:51 UTC, H. S. Teoh wrote:
> On Fri, Dec 07, 2018 at 05:11:35PM +0000, Atila Neves via Digitalmars-d wrote: [...]
>> Every language change is a cost, and therefore should justify its inclusion.  I personally don't think that it is in this case just to make a niche use case slightly easier, and this coming from someone from that niche! From now on it's:
>> 
>> const code = mixin(interp!q{
>>     // stuff that I used to write with many %s and it was hard to match them
>> up
>> });
>
> One thing I like about format() that string interpolation doesn't (easily) give, is the ability to specify additional formatting parameters, e.g., %6.2f, or %04X. You'd have to basically "uglify" string interpolation in much the same ways in order to get equivalent functionality, e.g., (hypothetical syntax) "Price is ${6.2:price}.".
>
> The one thing missing from format(), as you point out above, is the lack of named arguments for long format strings.  This could be fixed by extending format() with named placeholders, so that you can do something like:
>
> 	// Hypothetical syntax
> 	format(q"CODETEMPLATE
> 		void %{funcName}s(%{funcParams}(%s, %)) {
> 			%{boilerPlate}s
> 			for (i; 0 .. %{numIter}d)
> 			{
> 				%{loopBody}s
> 			}
> 		}
> 	CODETEMPLATE", NamedArgs([
> 		"funcName", "myFunc",
> 		"funcParams", ["int x", "float y", "string z"],
> 		"boilerPlate", generateBoilerplate(),
> 		"loopBody", generateFuncBody(),
> 		"numIter", 255
> 	]));
>
> Basically, instead of sequential arguments (or numerically-indexed arguments like $#1), format takes what amounts to a polymorphic associative array from which it can look up named arguments. This gets rid of the "too many %s I can't figure out which argument is which" problem, plus it makes your format string more readable and maintainable. It allows the same argument to be referenced multiple times, and arguments don't have to appear in the order they appear in the format string (important for i18n uses).
>
> Note that NamedArgs can be dispensed with if the language supported
> named arguments (like in Python, `myfunc(width = 123, height = 234)`).
>
>
> T

Got that one covered:

import std.stdio;

auto formatHex(T)(T value)
{
    static struct Formatter
    {
        T value;
        void toString(scope void delegate(const(char)[]) sink)
        {
            import std.format : formattedWrite;
            writef("%x", value);
        }
    }
    return Formatter(value);
}

void main(string[] args)
{
    int a = 42;
    writeln("a is ", a);
    writeln("a is ", a.formatHex);
    /*
    writeln(i"a is $(a)");
    writeln(i"a is $(a.formatHex)");
    */
}

December 07, 2018
On 12/7/18 1:26 PM, H. S. Teoh wrote:
> On Fri, Dec 07, 2018 at 05:11:35PM +0000, Atila Neves via Digitalmars-d wrote:
> [...]
>> Every language change is a cost, and therefore should justify its
>> inclusion.  I personally don't think that it is in this case just to
>> make a niche use case slightly easier, and this coming from someone
>> from that niche! From now on it's:
>>
>> const code = mixin(interp!q{
>>      // stuff that I used to write with many %s and it was hard to match them
>> up
>> });
> 
> One thing I like about format() that string interpolation doesn't
> (easily) give, is the ability to specify additional formatting
> parameters, e.g., %6.2f, or %04X. You'd have to basically "uglify"
> string interpolation in much the same ways in order to get equivalent
> functionality, e.g., (hypothetical syntax) "Price is ${6.2:price}.".

You'd need a valid D expression there with the given proposal (to lower to a tuple). Something like ${price.formatted!("%6.2s")}.

Or as I had suggested elsewhere, you could use UDAs:

@formatted!("%6.2s") double price;

It's not exactly easy, but it's doable. Plus you could alias it:

alias moneyf = formatted!("%6.2s");
${price.moneyf}

Which makes things actually quite pleasant.

> 
> The one thing missing from format(), as you point out above, is the lack
> of named arguments for long format strings.  This could be fixed by
> extending format() with named placeholders, so that you can do something
> like:
> 
> 	// Hypothetical syntax
> 	format(q"CODETEMPLATE
> 		void %{funcName}s(%{funcParams}(%s, %)) {
> 			%{boilerPlate}s
> 			for (i; 0 .. %{numIter}d)
> 			{
> 				%{loopBody}s
> 			}
> 		}
> 	CODETEMPLATE", NamedArgs([
> 		"funcName", "myFunc",
> 		"funcParams", ["int x", "float y", "string z"],
> 		"boilerPlate", generateBoilerplate(),
> 		"loopBody", generateFuncBody(),
> 		"numIter", 255
> 	]));
> 
> Basically, instead of sequential arguments (or numerically-indexed
> arguments like $#1), format takes what amounts to a polymorphic
> associative array from which it can look up named arguments. This gets
> rid of the "too many %s I can't figure out which argument is which"
> problem, plus it makes your format string more readable and
> maintainable. It allows the same argument to be referenced multiple
> times, and arguments don't have to appear in the order they appear in
> the format string (important for i18n uses).

The benefit I see from named parameters is using them more than once. That snippet above is very unreadable IMO.

But just for fun, let's compare to string interpolation strawman:

	text(iq"CODETEMPLATE
		void ${funcName}(${funcParams.formatted!"%-(%s, %)"}) {
			${generateBoilerplate}
			for (i; 0 .. ${numIter})
			{
				${generateFuncBody}
			}
		}
	CODETEMPLATE");

Oh, and it requires zero library support. It just works, as if you called it with the parameters in the right place.

With:

alias argf = formatted!("%-(%s, %)";

then the function declaration becomes:
		void ${funcName}(${funcParams.argf}) {

-Steve