February 04, 2021
On Thursday, 4 February 2021 at 11:00:24 UTC, claptrap wrote:
> i"i ate $apples apples today"

Note that we chose the sequence ${expr} to avoid duplicating standard uses of symbols inside strings (using just '$' alone would trigger a lot of unintentional usage, considering its use as a denomination specifier, or as a substitute for array length for mixin code)
February 04, 2021
On Thursday, 4 February 2021 at 12:27:49 UTC, Imperatorn wrote:
> 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

Note that we chose the sequence ${expr} to avoid duplicating standard uses of symbols inside strings.

This next bit isn't in the dip but Just using {} alone would conflict with tons of D syntax.

mixin(iq{ void foo() { is this a function body or an interpolated section? } });

February 04, 2021
On Thursday, 4 February 2021 at 12:56:14 UTC, Adam D. Ruppe wrote:
> On Thursday, 4 February 2021 at 12:27:49 UTC, Imperatorn wrote:
>> 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
>
> Note that we chose the sequence ${expr} to avoid duplicating standard uses of symbols inside strings.
>
> This next bit isn't in the dip but Just using {} alone would conflict with tons of D syntax.
>
> mixin(iq{ void foo() { is this a function body or an interpolated section? } });

That would be like an interpolated verbatim string.

"iq" would be roughly equal to "$@" in that case.

In the example you provided, it depends on what character is used. If { and } are used (instead of $), you need to escape those by { and }.

Example in C# ("@" removed from "@$" because no escape characters are present, also, since 8.0 the order doesn't matter, ie @$ EQU $@):

1. string s = $"{ void foo() { is this a function body or an interpolated section? } }";
2. string s = $"{{ void foo() { is this a function body or an interpolated section? } }}";

In case 1 the entire expression is evaluated, in case 2 the beginning and end curly brackets are "escaped" and therefore the inner expression is evaluated.

More information can be found here:
https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation
https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting
February 04, 2021
On Thursday, 4 February 2021 at 07:09:07 UTC, Daniel N wrote:
> 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).

That forces CTFE evaluation of *everything* which is pretty useless.

int bar;
fun!(i"foo {$bar+5}"); // bar cannot be evaluated at compile time

Only the literals themselves should be in the compile time tuple, with the rest of them staying right where they are (which can be either side). It is impossible to express that in a library, since to call a library function/instantiate a library template, you must choose one or the other! (The exception being if the lib function works only in terms of strings, and you use mixin at the call site.)

That's really the reason why this is proposed a language feature in the first place. A library *can't* do it well.

> 3) Unavoidable Bloat <- Current DIP1036

There's a lot of FUD around the community about templates lately and while there's a kernel of truth to it, it is massively overblown.

The only part of the template that is kept at runtime at all is the toString method, and it is trivial - return string literal. At the usage point, it is inlined, so I guess it only keeps the function body in case someone takes an address of it. A higher quality implementation (perhaps an optimized build already does, i've only tested my poc on dmd w/o flags) could skip this too and the template itself is invisible in the binary.

Inside the compiler, this template instance does add a couple entries to the symbol table, but they tend to be very small (proportional to the size of the string literal fragment in the original code). Where huge symbols become problematic is when they are recursive or otherwise nested since that's non-linear generated growth. It is rarely, if ever, the result of linear growth in the actual written codebase. (Of course, you could write some ctfe function that mixes in a huge interpolated string that mixes in another huge interpolated string but you can deliberately sabotage ctfe in all kinds of other ways too.)

Now, there is something to be said about functions receiving it, since i"foo" and i"bar" are different types, so you'd have two expansions. Of course, that's also the case with anything else you spread too... send X and const X and you get to separate instances. D in general could use a "collapse these instances" feature.

But even with the status quo, if you just forward converted arguments to another function to do the bulk of the work, this can also be pretty easily optimized by existing compilers.

void writeln(T...)(T t) {
    foreach(arg; t)
       write(arg.toString);
    write("\n");
}

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

Without the string in the compile time tuple from the language, you lost most the potential power.
February 04, 2021
On Thursday, 4 February 2021 at 13:37:32 UTC, Imperatorn wrote:
> In the example you provided, it depends on what character is used. If { and } are used (instead of $), you need to escape those by { and }.

Don't you see how that would make the string uglier and harder to use correctly by forcing escaping of an extremely common sequence? Remember, this is used with D code, along side curly brace syntax and the $ operator in slices, as well as for ordinary strings that use $ to mean a bunch of other things.

${} is unambiguous in token strings and much more clear in normal strings.
February 04, 2021
On Thursday, 4 February 2021 at 13:49:30 UTC, Adam D. Ruppe wrote:
> On Thursday, 4 February 2021 at 13:37:32 UTC, Imperatorn wrote:
>> In the example you provided, it depends on what character is used. If { and } are used (instead of $), you need to escape those by { and }.
>
> Don't you see how that would make the string uglier and harder to use correctly by forcing escaping of an extremely common sequence? Remember, this is used with D code, along side curly brace syntax and the $ operator in slices, as well as for ordinary strings that use $ to mean a bunch of other things.
>
> ${} is unambiguous in token strings and much more clear in normal strings.

Yup. Agreed, just thought I'd share how it works in C# for reference.
February 04, 2021
On Thursday, 4 February 2021 at 09:22:38 UTC, Luhrel wrote:
> In Python, there is just the dollar sign less...

Yes, and that makes a big difference in terms of visual clutter. It also provides string formatting that is close to printf.

f'{year}-{month:02}-{day:02}'

Very little visual clutter. If it looks worse than this, then I won't use it...


February 04, 2021
On Thursday, 4 February 2021 at 10:38:00 UTC, ddcovery wrote:
> 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:

f'{a} plus {b} is {a+b}'


February 04, 2021
On Thursday, 4 February 2021 at 12:56:14 UTC, Adam D. Ruppe wrote:
> This next bit isn't in the dip but Just using {} alone would conflict with tons of D syntax.

No? Even a lexer can disambiguate? Just count pairs of braces. You know that the opening brace is interpolating, so then you know what the closing one is.



February 04, 2021
On Thursday, 4 February 2021 at 15:29:47 UTC, Ola Fosheim Grøstad wrote:
> You know that the opening brace is interpolating, so then you know what the closing one is.

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?

With ${} there's no question.


mixin(iq{ void ${name}() {bar} });

name is interpolated, bar is a body.