www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP 1027--String Interpolation--Final Review Discussion Thread

reply Mike Parker <aldacron gmail.com> writes:
This is the discussion thread for the Final Review of DIP 1027, 
"String Interpolation":

https://github.com/dlang/DIPs/blob/d8f2e769c3a8c711e7886ccecc93eac9795dae9c/DIPs/DIP1027.md

This is the first review in which we are operating under the new 
procedure of using separate threads for discussion and feedback. 
Please see my blog post on the topic:

https://dlang.org/blog/2020/01/26/dip-reviews-discussion-vs-feedback/

Here in the discussion thread, you are free to discuss anything 
and everything related to the DIP. Express your support or 
opposition, debate alternatives, argue the merits... in other 
words, business as usual.

However, if you have any specific feedback for how to improve the 
the proposal itself, then please post it in the feedback thread. 
The feedback thread will be the source for the review summary I 
write at the end of this review round. I will post a link to that 
thread immediately following this post. Just be sure to read and 
understand the Reviewer Guidelines before posting there:

https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md

The review period will end at 11:59 PM ET on February 13, or when 
I make a post declaring it complete. Discussion in this thread 
may continue beyond that point.

Please stay on topic here. I will delete posts that are 
completely off topic.
Jan 30
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Thursday, 30 January 2020 at 09:46:38 UTC, Mike Parker wrote:

 However, if you have any specific feedback for how to improve 
 the the proposal itself, then please post it in the feedback 
 thread.
The feedback thread is here: https://forum.dlang.org/post/vhsfewrgycdeyynqdxnc forum.dlang.org
Jan 30
prev sibling next sibling parent reply Seb <seb wilzba.ch> writes:
On Thursday, 30 January 2020 at 09:46:38 UTC, Mike Parker wrote:
 This is the discussion thread for the Final Review of DIP 1027, 
 "String Interpolation":

 https://github.com/dlang/DIPs/blob/d8f2e769c3a8c711e7886ccecc93eac9795dae9c/DIPs/DIP1027.md


 Here in the discussion thread, you are free to discuss anything 
 and everything related to the DIP. Express your support or 
 opposition, debate alternatives, argue the merits... in other 
 words, business as usual.
Well, I guess the discussions and reviews will continue to boil down around Walter's position for tuples:
 Much of the discussion revolved around the syntax and the 
 choice of lowering to a tuple of format string and arguments, 
 and questions about interaction with other language features 
 (e.g., templates, token strings). The DIP author's position, 
 established over several comments, was essentially:
    tuples can easily be manipulated to suit the user's needs
    the implementation must be compatible with BetterC, meaning 
 printf and similar C functions
    the implementation must not trigger GC allocations
    the implementation must not depend on Phobos
    the implementation must be performant
Structs are essentially just fancy tuples and look the same when stored in memory. Hence, I don't see why all of the above can't be done by a templated struct? A templated struct would give the user a lot more options through additional opt-in methods and allow APIs to overload for them (e.g. SQL).
Jan 30
next sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Thursday, 30 January 2020 at 12:39:39 UTC, Seb wrote:
 Structs are essentially just fancy tuples and look the same 
 when stored in memory. Hence, I don't see why all of the above 
 can't be done by a templated struct?
 A templated struct would give the user a lot more options 
 through additional opt-in methods and allow APIs to overload 
 for them (e.g. SQL).
I reckon the answer is that it won't work with printf et al. Which I find ironic because I believe the people who want interpolated strings most badly don't use printf at all. That is of course always the problem of catering to a large group. Still, it might just be that a templated struct will open up more options with more upside than the downside of it not supporting printf.
Jan 30
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 30 January 2020 at 13:20:47 UTC, Sebastiaan Koppe 
wrote:
 That is of course always the problem of catering to a large 
 group.
A struct is the best option for a large group since it can add methods to handle different scenarios. Like I said in the last thread, it is fairly easy to make it work with a struct: i"printf works too: %n2 %n\n".c.tupleof Yes, you must call a method on it and explicitly do tupleof under current language rules. But it *is* possible and not even that weird. And if you don't do that, the compiler will throw a helpful type error message that could even tell you what to do! Then this is also possible several other things too. In my file, I also show that: string s = i"..."; // just works, via `alias toString this;` writeln(i"..."); // just works; writeln calls the toString method too other_random_function(i"..."); // other_random_function can recognize the special type and do various library things to it too! See my poc here: http://arsdnet.net/inter.d just pretend the `mixin(interpolate!(` wasn't there, since under the new DIP the language would do that part instead of the library. We CAN have it all.
Jan 30
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 30.01.20 14:34, Adam D. Ruppe wrote:
 
 i"printf works too: %n2 %n\n".c.tupleof
 
 Yes, you must call a method on it and explicitly do tupleof under 
 current language rules.
Maybe not under future language rules: https://github.com/tgehr/dmd/commit/bdb40fa96c471a7ace84596511a27ba3e803214f
Jan 30
prev sibling parent reply Seb <seb wilzba.ch> writes:
On Thursday, 30 January 2020 at 13:20:47 UTC, Sebastiaan Koppe 
wrote:
 On Thursday, 30 January 2020 at 12:39:39 UTC, Seb wrote:
 Structs are essentially just fancy tuples and look the same 
 when stored in memory. Hence, I don't see why all of the above 
 can't be done by a templated struct?
 A templated struct would give the user a lot more options 
 through additional opt-in methods and allow APIs to overload 
 for them (e.g. SQL).
I reckon the answer is that it won't work with printf et al.
Yeah, but it would: Option A) with .expand printf(i"...".expand) Option B) with an overload in druntime int printf(InterpolatedString)(InterpolatedString str) { return printf(str.expand) } printf(i"...") Both options would work in BetterC too.
 Which I find ironic because I believe the people who want 
 interpolated strings most badly don't use printf at all.
+1
 Still, it might just be that a templated struct will open up 
 more options with more upside than the downside of it not 
 supporting printf.
+1
Jan 30
parent MoonlightSentinel <moonlightsentinel disroot.org> writes:
On Thursday, 30 January 2020 at 13:37:17 UTC, Seb wrote:
 Option A) with .expand

 printf(i"...".expand)

 Option B) with an overload in druntime

 int printf(InterpolatedString)(InterpolatedString str) {
    return printf(str.expand)
 }

 printf(i"...")
Maybe even easier if dmd could handle a struct that decays into its members, e.g. an extended std.typecons.Tuple containing alias expand this; s.t. its members can be passed to printf & other functions automatically. (I recognize that `alias this` should be used carefully but this would avoid declaring overloads for every function that accepts some format string)
Jan 30
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/30/2020 4:39 AM, Seb wrote:
 Structs are essentially just fancy tuples and look the same when stored in 
 memory.
I've wanted to make tuples<=>structs. Here's the big problem - passing them as function parameters. It just doesn't work and be compatible with the C ABI.
Jan 30
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
One unification I was able to achieve is to make structs and arrays equivalent.

Another is delegates unify class member functions, struct member functions, and 
nested functions.
Jan 30
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Friday, 31 January 2020 at 02:29:15 UTC, Walter Bright wrote:
 On 1/30/2020 4:39 AM, Seb wrote:
 Structs are essentially just fancy tuples and look the same 
 when stored in memory.
I've wanted to make tuples<=>structs. Here's the big problem - passing them as function parameters. It just doesn't work and be compatible with the C ABI.
Could you clarify the problem? If you have a C function like pow: extern (C) double pow(double,double); Is the problem providing a way to call pow with one symbol? pow(s); So that's why D hasn't been able to unify structs/tuples? Because tuples auto-expand and structs do not?
Jan 31
parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/31/2020 3:37 AM, Jonathan Marler wrote:
 On Friday, 31 January 2020 at 02:29:15 UTC, Walter Bright wrote:
 It just doesn't work and be compatible with the C ABI.
Could you clarify the problem?
If you have a C function: void foo(char a, char b, char c); and pass it a: struct S { char a, b, c; } it won't be passed the same way.
Feb 01
prev sibling next sibling parent reply Seb <seb wilzba.ch> writes:
On Friday, 31 January 2020 at 02:29:15 UTC, Walter Bright wrote:
 On 1/30/2020 4:39 AM, Seb wrote:
 Structs are essentially just fancy tuples and look the same 
 when stored in memory.
I've wanted to make tuples<=>structs. Here's the big problem - passing them as function parameters. It just doesn't work and be compatible with the C ABI.
As mentioned earlier and by others: there are still two options (even without a solution for this) that would solve the C ABI problem. I'm failing to understand why they wouldn't work: 1) There are only a handful of C functions that would be able to receive this tuple (the printf family). Why can't we add an overload for them? 2) If (1) isn't an option, wouldn't printf(i"interpolated ...".c) work?
Jan 31
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/31/2020 6:46 AM, Seb wrote:
 As mentioned earlier and by others: there are still two options (even without
a 
 solution for this) that would solve the C ABI problem. I'm failing to
understand 
 why they wouldn't work:
 
 1) There are only a handful of C functions that would be able to receive this 
 tuple (the printf family). Why can't we add an overload for them?
Then they aren't really printf? C functions aren't overloadable?
 2) If (1) isn't an option, wouldn't printf(i"interpolated ...".c) work?
structs and tuples aren't interchangeable if you have to remember to do things like that.
Feb 01
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 2 February 2020 at 07:23:31 UTC, Walter Bright wrote:
 2) If (1) isn't an option, wouldn't printf(i"interpolated 
 ...".c) work?
structs and tuples aren't interchangeable if you have to remember to do things like that.
You don't really have to remember per se because you'll get a type mismatch error if you don't do it. The compiler error message could even recognize it and remind you in the error message to use the method. Will you sacrifice all the other use cases in their entirety just because `printf(i"".c)` is not 100% identical to `printf(i"")`? I don't see the logic in that. D is an all purpose language, not syntax sugar over C.
Feb 02
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.02.20 15:11, Adam D. Ruppe wrote:
 On Sunday, 2 February 2020 at 07:23:31 UTC, Walter Bright wrote:
 2) If (1) isn't an option, wouldn't printf(i"interpolated ...".c) work?
structs and tuples aren't interchangeable if you have to remember to do things like that.
You don't really have to remember per se because you'll get a type mismatch error if you don't do it. The compiler error message could even recognize it and remind you in the error message to use the method. Will you sacrifice all the other use cases in their entirety just because `printf(i"".c)` is not 100% identical to `printf(i"")`? I don't see the logic in that. D is an all purpose language, not syntax sugar over C.
This is a use case for `alias this`, the language just has to support it.
Feb 02
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 2 February 2020 at 14:20:16 UTC, Timon Gehr wrote:
 This is a use case for `alias this`, the language just has to 
 support it.
Indeed. So is string s = i""; so idk which one we'd use (unless the language gets multiple alias this lol). But regardless the struct lets us have it all. With alias this it is every minor detail. Without it it is a simple method call. I used to lukewarm support this DIP, but since the struct is so much better, superior in almost every objective measure, I now think we should vote it down unless it changes.
Feb 02
next sibling parent reply Tove <tove fransson.se> writes:
On Sunday, 2 February 2020 at 14:29:17 UTC, Adam D. Ruppe wrote:
 On Sunday, 2 February 2020 at 14:20:16 UTC, Timon Gehr wrote:
 This is a use case for `alias this`, the language just has to 
 support it.
Indeed. So is string s = i""; so idk which one we'd use (unless the language gets multiple alias this lol).
Implict conversion to string would trigger allocations! This is not an option, allocations has to be explict. However alias this to tuple is fine by me.
Feb 02
next sibling parent bachmeier <no spam.net> writes:
On Sunday, 2 February 2020 at 15:31:46 UTC, Tove wrote:

 Implict conversion to string would trigger allocations! This is 
 not an option
D is a GC language.
Feb 02
prev sibling next sibling parent reply MoonlightSentinel <moonlightsentinel disroot.org> writes:
On Sunday, 2 February 2020 at 15:31:46 UTC, Tove wrote:
 Implict conversion to string would trigger allocations! This is 
 not an option, allocations has to be explict. However alias 
 this to tuple is fine by me.
The GC is usually opt-in. Having this in feature in gc code would be better than not having it at all.
Feb 02
parent Tove <tove fransson.se> writes:
On Sunday, 2 February 2020 at 16:10:49 UTC, MoonlightSentinel 
wrote:
 On Sunday, 2 February 2020 at 15:31:46 UTC, Tove wrote:
 Implict conversion to string would trigger allocations! This 
 is not an option, allocations has to be explict. However alias 
 this to tuple is fine by me.
The GC is usually opt-in. Having this in feature in gc code would be better than not having it at all.
It's perfectly fine to have a explicit conversion to string, but it needs to be clearly visible from the code where all expensive operations are. It's the same design philosophy as with the 'in' operator, you shouldn't use 'in' with O(n) algos, because it looks like a deceptively cheap operation.
Feb 02
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sun, Feb 02, 2020 at 03:31:46PM +0000, Tove via Digitalmars-d wrote:
 Implict conversion to string would trigger allocations! 
indeed but you can also just not do that; you do need to specift string to trigger alias this. but i personally would also be oj with explicit toString call, my point is really with a struct we can do it all, whereas with the dip as-is we privilege one niche case at the expense of almost all others.
Feb 02
parent Tove <tove fransson.se> writes:
On Sunday, 2 February 2020 at 16:21:18 UTC, Adam D. Ruppe wrote:
 On Sun, Feb 02, 2020 at 03:31:46PM +0000, Tove via 
 Digitalmars-d wrote:
 Implict conversion to string would trigger allocations!
indeed but you can also just not do that; you do need to specift string to trigger alias this. but i personally would also be oj with explicit toString call, my point is really with a struct we can do it all, whereas with the dip as-is we privilege one niche case at the expense of almost all others.
Yes, but my primary concern is calling functions with string parameters. Otherwise I fully agree with you, I prefer your struct solution with added explict toString and implicit tuple expansion.
Feb 02
prev sibling parent reply Dennis <dkorpel gmail.com> writes:
On Sunday, 2 February 2020 at 15:31:46 UTC, Tove wrote:
 Implict conversion to string would trigger allocations! This is 
 not an option, allocations has to be explict.
Memory allocation is an essential tool to make many language features like array literals and nested functions just work. Because of the garbage collector, this can be ` safe` without leaks too, which is a good quality of D.
 It's the same design philosophy as with the 'in' operator, you 
 shouldn't use 'in' with O(n) algos, because it looks like a 
 deceptively cheap operation.
`in` has worst-case time complexity O(n) for associative arrays, and in many practical cases an O(n) linear scan is faster than a complex O(log n) search algorithm. Also, the concatenation operator ~ is O(n) and triggers allocations. D is not C where every operation is supposed to compile to a handful of machine instructions at most. If you write: string c = i"name: $firstname $lastname"; I don't think anyone would expect this to be O(1) without allocations and dislike the language for making this simply work in the best possible way. If you want it to not work unless no allocations are made, you can use nogc.
Feb 02
parent reply Tove <tove fransson.se> writes:
On Sunday, 2 February 2020 at 18:07:00 UTC, Dennis wrote:
 On Sunday, 2 February 2020 at 15:31:46 UTC, Tove wrote:
 Implict conversion to string would trigger allocations! This 
 is not an option, allocations has to be explict.
Memory allocation is an essential tool to make many language features like array literals and nested functions just work. Because of the garbage collector, this can be ` safe` without leaks too, which is a good quality of D.
 It's the same design philosophy as with the 'in' operator, you 
 shouldn't use 'in' with O(n) algos, because it looks like a 
 deceptively cheap operation.
`in` has worst-case time complexity O(n) for associative arrays, and in many practical cases an O(n) linear scan is faster than a complex O(log n) search algorithm. Also, the concatenation operator ~ is O(n) and triggers allocations.
The point is, it should be a fast operation, my conclusion still stands.
 D is not C where every operation is supposed to compile to a 
 handful of machine instructions at most.
 If you write:

 string c = i"name: $firstname $lastname";

 I don't think anyone would expect this to be O(1) without 
 allocations and dislike the language for making this simply 
 work in the best possible way.

 If you want it to not work unless no allocations are made, you 
 can use  nogc.
Sure nogc works fine, but sometimes you want to use gc and when you do, the language should help you to do the right thing. while(...) my_fun(i"name: $firstname $lastname"); If my_fun takes a string this would be terrible code, it should not compile until you make a conscious choice: a) Performance is not important for my app. -> Let's add toString() b) Add a new overload to my_fun that can handle interpolated strings directly.
Feb 02
parent reply Dennis <dkorpel gmail.com> writes:
On Sunday, 2 February 2020 at 18:43:20 UTC, Tove wrote:
 Sure  nogc works fine, but sometimes you want to use gc and 
 when you do, the language should help you to do the right thing.

 while(...)
   my_fun(i"name: $firstname $lastname");

 If my_fun takes a string this would be terrible code, it should 
 not compile until you make a conscious choice:
 a) Performance is not important for my app. -> Let's add 
 toString()
 b) Add a new overload to my_fun that can handle interpolated 
 strings directly.
You know, maybe instead of trying to give D the reputation of a mechanically checked memory safe language, it should be marketed with something better: Mechanically enforced premature optimization! ;) In all seriousness, I understand that eagerly constructing new strings is a bad practice for performant code and that you want to discourage it. I just think that since D is also pretty good for quick scripts or fast prototyping, being able to quickly type an interpolated string to evaluate to `string` would be very convenient. Personally I especially dislike always importing std.conv: text or std.string: format just to get an informative message for `throw new Exception("...")` or `assert(x, "...")`, so I hope interpolated strings can at least solve that.
Feb 02
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 2 February 2020 at 19:22:23 UTC, Dennis wrote:
 In all seriousness, I understand that eagerly constructing new 
 strings is a bad practice for performant code and that you want 
 to discourage it. I just think that since D is also pretty good 
 for quick scripts or fast prototyping, being able to quickly 
 type an interpolated string to evaluate to `string` would be 
 very convenient.
Yeah. Though see my last email for another point on this re encoding for different contexts. And take note that with the struct proposal, it will not require an import. I'd implement it like: string toString()() { import std.conv; return text(args); } so the import is internal to it (so you don't have to write it yourself), and being templated, the import is only triggered if the method is actually used (so you don't pay for it in cases like -betterC where you don't want it). note we can also offer the toString(scope delegate() sink) version for working with those parts of the library that are allocation averse too. So many of these problems are already solved in D library techniques!
Feb 02
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/20 9:29 AM, Adam D. Ruppe wrote:
 On Sunday, 2 February 2020 at 14:20:16 UTC, Timon Gehr wrote:
 This is a use case for `alias this`, the language just has to support it.
Indeed. So is string s = i""; so idk which one we'd use (unless the language gets multiple alias this lol). But regardless the struct lets us have it all. With alias this it is every minor detail. Without it it is a simple method call. I used to lukewarm support this DIP, but since the struct is so much better, superior in almost every objective measure, I now think we should vote it down unless it changes.
The problem I have with the struct mechanism is that it enforces the parameters are all non-reffable data. e.g., this should work with the DIP as proposed: int apples; int bananas; readf(i"I have $apples apples and $bananas bananas"); I don't know how you do that with a struct as the result of the interpolated string without knowing how the parameters will be used. Or this: struct S {} foo!(i"I'm passing $S as an alias/type, with some formatting data around it"); Other than that, I like the idea of the struct for the purposes of overloading. I still would like the format string to be a custom type that devolves to string. I also liked the straight conversion to a tuple where every other parameter was a string (i.e. Marler's implementation). The biggest thing that the DIP has going for it is that there are lots of functions which have a format string + args, due to the way D/C does varargs, and this will be a drop-in call without having to change any code or add special overloads. The whole printf thing is meh to me, I don't use it and don't care to use it with this mechanism anyway (writef is a different story). And Walter: C functions aren't overloadable? This works just fine: --------- import core.stdc.stdio : printf; void printf(int x) { printf("%d", x); } void main() { printf("hello %d\n", 5); printf(5); printf("\n"); } --------- output: hello 5 5 -Steve
Feb 02
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 2 February 2020 at 18:46:36 UTC, Steven Schveighoffer 
wrote:
 The problem I have with the struct mechanism is that it 
 enforces the parameters are all non-reffable data.
This is why I at first wanted a pure language solution instead of the hybrid solution now (language provides syntax sugar, library provides implementation), but I'm not actually sure this is worth fighting over.
 int apples;
 int bananas;
 readf(i"I have $apples apples and $bananas bananas");
It is kinda cool that this can work, but really, do you think it is legitimately useful? readf is so weird in how it works that you'd very rarely find a case where the interpolated string even does the right thing. Moreover, this would also require ref; it wouldn't work with scanf. So that limits it either more. But consider if you did want to do something like this, you could i"$(&apples)"... which actually would work with scanf as well as with the struct; a pointer can go in there easily enough.
 struct S {}
 foo!(i"I'm passing $S as an alias/type, with some formatting 
 data around it");
Again, I think that is cool but not useful enough to justify compromising other cases. I'll take it if and only if it comes for free, and the naked tuple does not come for free. A pure language struct btw can do this - the compiler, instead of calling a library function, just creates the type internally. Then it can declare them as aliases as needed. But that opens up other complications.... and really, what's the value? Why would you want an alias in the middle of a string? There might be some... but I can't think of one right now and I suspect there'd be a better way anyway. But let's just be careful not to damage real world use cases in the name of "that might be cool in theory someday". The struct has definite real world use cases... the alias/ref tuples not so sure.
 The biggest thing that the DIP has going for it is that there 
 are lots of functions which have a format string + args
Yeah, I do like being able to specify a format thingy (`${%3d}bananas`) in there. That's something I'd definitely see being legitimately used. Though I'd probably prefer the format string to be built in a library, I like that bit enough that I want the magic lowering to do something with it somehow. But otherwise it is easy for a library solution to provide format strings as needed, transparently to the user. And a library solution can handle the DIP's limitation: string tool = "hammer"; writefln(i"hammering %s with $tool", "nails"); How? Well, since it is an independent object, it knows what arguments it had! It could translate itself before going to outside uses. And especially with a method to produce a format string, it can even be smart enough to escape % to %% in the interpolated things. So that becomes writefln(_interp!("hammering %s with ", "")(tool), "nails"); and then _interp returns its helper struct. Well, if we do implicit toString and tool == "%s" well, lol we just poisoned our format string. But at the same time, we could provide a method to escape the interpolated thing - and writefln could overload based on this to just call that. writeln knows what it needs. So it'd be like void writefln(__Interp fmt, ...) { string f = fmt.toEscapedString!(a => a.replace("%", "%%")); // forward back to the normal one writefln(f, ...); } Then that toEscapedString method on the struct calls the given delegate on each user string as it is appended to the final result. Allowing us to properly encode it in a particular context. You could us the same thing with html: string user = "<try_injection>"; string myHtml = i"<b>$user</b>".toEscapedString!htmlEntitesEncode; // myHtml == "<b>&lt;try_injection&gt;</b>" and ditto for any format you can imagine. The struct is flexible in so many ways that a tuple isn't! And it is easy to use. Javascript's string interpolation even lets us do this. Do we want D to be defeated by JAVASCRIPT?!!?!?!?! lol (this kind of thing btw is another argument against an implicit toString on alias this. Like I propose that to be friendly to people who insist `string s = i"$foo";` must work, and it is cool that we can, though I personally think it should be a decision. I don't care about GC, but I do care about proper encoding of output.) Again, back to your main counter point, do we want to sacrifice all this *definite* value for possible uses in theory for ref arguments? I'm not against refness per se (I do find it a lil weird, but still)... just I don't think it is worth sacrificing anything for since the to-string potential is far more clear than the from-string readf potential.
Feb 02
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/20 2:36 PM, Adam D. Ruppe wrote:
 On Sunday, 2 February 2020 at 18:46:36 UTC, Steven Schveighoffer wrote:
 The problem I have with the struct mechanism is that it enforces the 
 parameters are all non-reffable data.
This is why I at first wanted a pure language solution instead of the hybrid solution now (language provides syntax sugar, library provides implementation), but I'm not actually sure this is worth fighting over.
 int apples;
 int bananas;
 readf(i"I have $apples apples and $bananas bananas");
It is kinda cool that this can work, but really, do you think it is legitimately useful? readf is so weird in how it works that you'd very rarely find a case where the interpolated string even does the right thing.
I'm sure I would rather use that than readf with format string and trailing parameters. And why would it "rarely" do the right thing?
 Moreover, this would also require ref; it wouldn't work with scanf. So 
 that limits it either more.
 
 But consider if you did want to do something like this, you could 
 i"$(&apples)"... which actually would work with scanf as well as with 
 the struct; a pointer can go in there easily enough.
Well, yeah. It's not limiting scanf, because scanf uses pointers. Either solution works there. But not for something like readf.
 
 struct S {}
 foo!(i"I'm passing $S as an alias/type, with some formatting data 
 around it");
Again, I think that is cool but not useful enough to justify compromising other cases. I'll take it if and only if it comes for free, and the naked tuple does not come for free.
I don't know for sure what possibilities are unlocked by this, but I know that the struct implementation has limitations that the naked tuple does not. Avoiding limitations means we could potentially find something genuinely useful that would have been blocked with something that has limitations. I think we could get the best of both worlds if the interpolated string itself was not just a string, but rather a library-defined type (well something slightly more special -- it should implicitly cast to a null-terminated immutable(char)* if needed, just like string literals). You'd get overloading capabilities to do something custom with interpolated strings, and you would get all the niceties you would get from the struct solution, but without the limitations.
 A pure language struct btw can do this - the compiler, instead of 
 calling a library function, just creates the type internally. Then it 
 can declare them as aliases as needed. But that opens up other 
 complications.... and really, what's the value? Why would you want an 
 alias in the middle of a string? There might be some... but I can't 
 think of one right now and I suspect there'd be a better way anyway.
I don't think this is the right route either. Putting "special" types into the language is something we're trying to move away from (it irks me still that the AA is not yet a library type, and needs TypeInfo to work).
 But let's just be careful not to damage real world use cases in the name 
 of "that might be cool in theory someday". The struct has definite real 
 world use cases... the alias/ref tuples not so sure.
The readf example is a real world use case.
 But otherwise it is easy for a library solution to provide format 
 strings as needed, transparently to the user. And a library solution can 
 handle the DIP's limitation:
 
 string tool = "hammer";
 writefln(i"hammering %s with $tool", "nails");
This can be handled if the iterpolated string has a different type than a normal string, allowing overloading.
 You could us the same thing with html:
 
 string user = "<try_injection>";
 
 string myHtml = i"<b>$user</b>".toEscapedString!htmlEntitesEncode;
 // myHtml == "<b>&lt;try_injection&gt;</b>"
This can work with the DIP as-is. EscapedString toEscapedString(EncodingStyle, T...)(string escapedFormat, T items)
 and ditto for any format you can imagine. The struct is flexible in so 
 many ways that a tuple isn't! And it is easy to use.
Both have benefits, and both have limitations. I feel the limitations of the struct are that it doesn't allow whole categories of usage. The limitations of the tuple are that you'll have to parse the generated string again after the compiler already has done it (and again, this can be solved with a new type for the string).
 Javascript's string interpolation even lets us do this. Do we want D to 
 be defeated by JAVASCRIPT?!!?!?!?! lol
I think anything javascript does with interpolated strings, we can do. We just have to write the function to do it.
 Again, back to your main counter point, do we want to sacrifice all this 
 *definite* value for possible uses in theory for ref arguments? I'm not 
 against refness per se (I do find it a lil weird, but still)... just I 
 don't think it is worth sacrificing anything for since the to-string 
 potential is far more clear than the from-string readf potential.
I see no difference in saying "you can access printf, you just need to put in a little .c at the end" vs. "you can access assignment to string, you just have to put a little .format at the end". The to-string potential is just fine with the DIP. That's mainly what it's for. On the to-string side, both ideas have merits and drawbacks, and I view them quite ambivalently. The from-string (as you call it) or compile-time potential is non-existent in the "interpolated-string-to-struct" idea, so it's a clear win for something that gives you a tuple of what you passed in. I would vote a resounding yes to this DIP + making the interpolated string a new type, but still a solid yes on the DIP. I would not vote no on the struct idea, but I would not be as positive about it. And we can actually add later the idea of making the interpolated string a new type after this DIP is implemented, mitigating a lot of the concern here. I don't want to let perfect be the enemy of good. -Steve
Feb 02
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
I'm dropping other stuff just cuz I don't want to argue down 
rabbit holes. Let's see what common ground we can find and build 
upon that.

On Sunday, 2 February 2020 at 20:13:36 UTC, Steven Schveighoffer 
wrote:
 I think we could get the best of both worlds if the 
 interpolated string itself was not just a string, but rather a 
 library-defined type (well something slightly more special -- 
 it should implicitly cast to a null-terminated immutable(char)* 
 if needed, just like string literals).
Yes, indeed, I proposed this in the last round too. I think it is our best bet for a compromise to salvage this DIP. You would lose the methods on it ... however, we have UFCS, so not a deal breaker. Lots of the community is opposed to the requirement to import something for the UFCS flavor; that is a psychological barrier for many people. I'm not in love with that myself, but I can live with it. And I remain legit concerned about implicit conversions though: printf(i"$some_int"); that looks OK, but SILENTLY DOES THE WRONG THING. But if dmd warned on the format specifier like gcc does, we're cool. Could be an enhancement later. I warn about this but do not withhold support about it. The DIP mentions this one: `printf("%s $something", foo);` This also does the wrong thing. But so does `printf(i"$something% complete");`, since `% c` is a valid format specifier.... yet it doesn't look like one, especially since the interpolated string use $ now. We need to make % go ahead and get translated to %% by the compiler so the parsing function can still work sanely with it. That's the solution to this. If the compiler makes % magic in the generated string, it needs to encode % in the user input too. Then put a type on the format string, if it implicitly converts is up to y'all. I just need some way to detect the format string's presence via in the type system for overloading existing functions. Do those two small changes and this DIP will have my support. * specify that % in the user string gets translated to %% in the format literal * put the format literal in a wrapper type. i"$foo %" is translated to the tuple: __d_format_literal!("%s %%"), foo struct __d_format_literal(string fmt) { enum f = fmt; alias f this; } That implicitly converts and just works in printf. That's the answer.
 And we can actually add later the idea of making the 
 interpolated string a new type after this DIP is implemented,
As soon as this DIP is implemented, we're frozen. Any future change will face additional barriers as a breaking change. It is going to be a lot better to just do it at least somewhat correctly now. gah i've been trying to edit this post for like 90 mins now. im just gonna hit send; i 2g2 and i think you get the gist of what i mean.
Feb 02
next sibling parent reply Arine <arine123445128843 gmail.com> writes:
On Sunday, 2 February 2020 at 23:54:35 UTC, Adam D. Ruppe wrote:
 Do those two small changes and this DIP will have my support.

 * specify that % in the user string gets translated to %% in 
 the format literal
 * put the format literal in a wrapper type.

 i"$foo %"

 is translated to the tuple:

 __d_format_literal!("%s %%"), foo


 struct __d_format_literal(string fmt) {
         enum f = fmt;
         alias f this;
 }

 That implicitly converts and just works in printf. That's the 
 answer.
I don't think you should compromise. Using printf as the basis just limits what can be done with an interpolated string. How do you handle custom types? The DIP doesn't go over this but it'd have to either disallow it completely. Or convert it to a string first using `.toString()`, or similar. Since printf() wouldn't be able to take a custom type. This inhibits any sort of user implementation or customization and forces the type to be converted to a string first, losing any additional detail and preventing possible optimizations in only allocating one buffer for the entire thing. For each object toString() would allocate it's own buffer, when it could be constructed in place. SqlValue value; ExecuteSettings settings; execute(settings, i"SELECT * FROM table WHERE value=$value"); void execute(InterpolatedString)(ref ExecuteSettings settings, InterpolatedString str) { string output; // static foreach(i; str.args) { // static if(is(typeof(str.args[i]) == SqlValue)) { // ... // typeof(str.args[i]) == SqlValue if( str.args[i].someProperty && settings.someSetting ) { // ... } str.args[i].emplaceToString(output); // or otherwise } printf(i"something $value".c); // ok, "c" would call toString() to make it compatible printf(i"something $value"); // or with overload for printf() Compared to how'd you have to implement it with the current DIP: // this implementation can be used with printf void execute(Args...)(ref ExecuteSettings settings, string format, Args args) { // typeof(args[0]) == string, can't determine what type it was // more difficult to parse format and verify it is valid // can't access SqlValue for properties as it is now just a string } printf(i"something $value"); // works, calls toString first Or to retain the type, it would then not work with printf(). void execute(Args...)(ref ExecuteSettings settings, string format, Args args) { // typeof(args[0]) == SqlValue // what specifier was inserted into format? %s? // what if we know we can check now and instead use %d? // now we have to be able to parse printf-style formats properly to change // the formatting to be more efficient and make more sense // // also can't do in place optimizations without completely re-implementing // sprintf or otherwise support all of printf format capabilities } printf(i"something $value"); // error, passing SqlValue printf(i"something $value.toString()"); // ok, but kind of counter intuitive Trying to cator to printf results in an implementation with the least amount of flexibility imo. It forces legacy debt onto the user to support. It will ultimately just be left to the handful of functions that use it now.
Feb 02
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 3 February 2020 at 02:57:41 UTC, Arine wrote:
 I don't think you should compromise. Using printf as the basis 
 just limits what can be done with an interpolated string. How 
 do you handle custom types?
Eh, that's a solved problem. See: https://wiki.dlang.org/Defining_custom_print_format_specifiers The format string is incredibly flexible and the tuple proposal does a fairly good job maintaining all available information until the last minute. With the % -> %% encoding, it is also possible to reliably* determine which * with the exception of a user-defined format string that breaks this convention. e.g. `i"${not_percent}(foo)"` will not be able to tell for certain that foo is tied to not_percent, and this can throw off processing of all subsequent arguments as well. Maybe we should do something about this.
 Since printf() wouldn't be able to take a custom type.
Yeah, printf would likely fail with custom types, but that's printf's limitation - D's writef works well with them, including various custom format specifiers and allocation-free output. And other custom functions, provided we identify the format string as such, can do any special parsing of it. With my proposed addendum, even compile-time validation of the format string is possible in consumer functions: void my_thing(Format, Args...)(Format fmt, Args args) { static if(is(Format == _d_interpolated_string!S, string S)) static assert(string_is_ok(S)); else static assert(0, "you didn't pass an interpolated string"); // use args according to fmt } (I would actually recommend we make the name and interface a wee bit more friendly in the library for user consumption, including methods that can be tested without tying us to just one argument like that is expression does above, for future expansion potential... like I kinda want to also say how many arguments there are as CT params there, or even the slices of that string that represent format strings - that would IMO be the holy grail as we can not only get how many args but exactly where they are without reparsing the string - but I digress. Regardless, the compiler can output the _d_whatever ugly thing and I like to use ugly things in these examples in an effort to avoid people arguing over the name when I'd rather focus on the underlying concept.) But anyway since the format string is actually part of the *type*, we can overload on it and extract the original string as a compile-time constant - including when passed as a runtime argument list - for further processing.
 For each object toString() would allocate it's own buffer, when 
 it could be constructed in place.
void toString( scope void delegate(const(char)[]) sink, FormatSpec!char fmt) is already possible with D's existing format function on custom objects. No allocation and you can respond to arbitrary formatting details as specified there. So your example:
 Or to retain the type, it would then not work with printf().
A custom type wouldn't work with printf with the DIP as it stands. It does not attempt to convert anything, it just forwards the arguments. Similarly your function could require that the format strings must just be %s and you don't support customization. The implementation there can be as simple as scanning forward for % and a compile-time template could break it up into an array for you with no runtime trouble. Or you could only support a custom format that the user needs to put in the ${here}(var) thingy. I'm *very* close to supporting this DIP, it isn't actually that bad. As it is, I'd have to recommend we defeat it, but if we can all agree to amend it to put in the template thing we can fix everything. Even with just the one string arg, we can do a lot with it... (my struct would make simple cases simpler - you can just .toString it without imports or even alias toString this - but the template does actually allow more custom flexibility.)
Feb 02
parent reply Arine <arine123445128843 gmail.com> writes:
On Monday, 3 February 2020 at 03:57:09 UTC, Adam D. Ruppe wrote:
 On Monday, 3 February 2020 at 02:57:41 UTC, Arine wrote:
 I don't think you should compromise. Using printf as the basis 
 just limits what can be done with an interpolated string. How 
 do you handle custom types?
Eh, that's a solved problem. See: https://wiki.dlang.org/Defining_custom_print_format_specifiers The format string is incredibly flexible and the tuple proposal does a fairly good job maintaining all available information until the last minute. With the % -> %% encoding, it is also possible to reliably* determine which * with the exception of a user-defined format string that breaks this convention. e.g. `i"${not_percent}(foo)"` will not be able to tell for certain that foo is tied to not_percent, and this can throw off processing of all subsequent arguments as well. Maybe we should do something about this.
 Since printf() wouldn't be able to take a custom type.
Yeah, printf would likely fail with custom types, but that's printf's limitation - D's writef works well with them, including various custom format specifiers and allocation-free output.
Part of the whole reason is that this works with printf(), the examples in the DIP almost exclusive use the C functions. Why stop short of custom types?
 And other custom functions, provided we identify the format 
 string as such, can do any special parsing of it.

 With my proposed addendum, even compile-time validation of the 
 format string is possible in consumer functions:

 void my_thing(Format, Args...)(Format fmt, Args args) {
     static if(is(Format == _d_interpolated_string!S, string S))
       static assert(string_is_ok(S));
     else
       static assert(0, "you didn't pass an interpolated 
 string");

    // use args according to fmt
 }
Implement string_is_ok() for me. How easy is it to implement to verify it is valid for your own purpose while also working around the printf formatting. Ensuring that it correctly interprets all of printf's features.
 that would IMO be the holy grail as we can not only get how 
 many args but exactly where they are without reparsing the 
 string - but I digress.
You can have that without the formatting, or having to add extra meta data so you can link which argument goes with what index in the format array. You get all of this if you don't use a printf-style formatting string. It doesn't have to be added ontop with a bandaid.
 But anyway since the format string is actually part of the 
 *type*, we can overload on it and extract the original string 
 as a compile-time constant - including when passed as a runtime 
 argument list - for further processing.

 For each object toString() would allocate it's own buffer, 
 when it could be constructed in place.
void toString( scope void delegate(const(char)[]) sink, FormatSpec!char fmt) is already possible with D's existing format function on custom objects. No allocation and you can respond to arbitrary formatting details as specified there.
The biggest problem here is that toString() then doesn't know or understand the context it is being used in. You'd have to write some sort of wrapper and then it starts to become convoluted. At that point you are just trying to imitate a solution that doesn't use printf formatting. That then has to use a delegate, and if you have to do something like insert one character at a time, it won't be ideal making so many calls like that. Especially if you are concerned with performance. Calling sink() multiple times could also cause reallocations. It all depends on how format/writef or whatever it is you are using is implemented. That generic function isn't going to know about it as well as whatever you are implementing. Such as if you want to implement a nogc solution. There's no phobos implementation for that. Your on your own to implement your own nogc solution and you're stuck having to implement this monolithic format spec. You can respond to arbitrary formatting, but what does the compiler choose to insert? The DIP makes no mention of this. These are all lacking details that shouldn't be plugged as they appear.
 So your example:

 Or to retain the type, it would then not work with printf().
A custom type wouldn't work with printf with the DIP as it stands. It does not attempt to convert anything, it just forwards the arguments.
Where did you get that? The DIP doesn't mention that. So what is the format string in this case? That's the first point of failure, when it has to decide what format specifier it has to put into the format string. Only the first example is what I'd deem good enough to implement. The other examples that use a printf-style format is showing the inadequacies that are created from using it.
 I'm *very* close to supporting this DIP, it isn't actually that 
 bad. As it is, I'd have to recommend we defeat it, but if we 
 can all agree to amend it to put in the template thing we can 
 fix everything. Even with just the one string arg, we can do a 
 lot with it...
You can't make your own nogc function with custom types so easily. This DIP seems to be hellbent on supporting C functions, but at the end of the day it won't be able to support nogc without having to completely create your own solution that implements printf-style formatting. That's not something anyone is likely to do. Supporting nogc would be a better goal than supporting the C functions. Just calling toString() for the C functions isn't sufficient. That's why people would want to use it with C functions, is so that it is nogc. You're fine with not having custom types work with C functions, but is that really fine? D doesn't have an easy alternative and requiring the printf format spec be utilized makes it incredibly more difficult to roll your own solution. It's ironic, this DIP is pushing for printf C functions to be usable so it can work with nogc. But it makes it more difficult to support nogc and your own custom implementations because phobos inadequately supports nogc. Even if it did have some nogc format, you'd still be impeded from optimizing your own implementation and would rely on whatever kind of optimizations are done in nogc format. Which might not fit your use case.
Feb 02
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 3 February 2020 at 05:10:13 UTC, Arine wrote:
 Part of the whole reason is that this works with printf(), the 
 examples in the DIP almost exclusive use the C functions. Why 
 stop short of custom types?
This DIP doesn't format them at all. It is fundamentally just a syntax sugar rewrite of a string into an argument list.
 Implement string_is_ok() for me.
That varies based on what features you choose to support. It could be as simple as assert(string.count("%s") == args.length) and you ignore anything else. Or you could go crazy with all kinds of custom specifiers. The compiler doesn't do anything except 1) replace $xxxx with %s in the string 2) blindly copy/paste what the user said ${xxxx} while moving the var to the argument list
 Ensuring that it correctly interprets all of printf's features.
This is not necessary. You can make your version only do the bare minimum.
 You can respond to arbitrary formatting, but what does the 
 compiler choose to insert? The DIP makes no mention of this.
The compiler ONLY uses %s unless the string's author specifies something else in the string itself. Anything else is up to the user - and the user is supposed to look up what the function they are calling supports. That's what this paragraph from the dip is about: === The {%d} syntax is for circumstances when the format specifier needs to be anything other than %s, which is the default. What goes between the { } is not specified, so this capability can work with future format specification improvements without needing to update the core language. It also makes interpolated strings agnostic about what the format specifications are. === Unless the person calling your function SPECIFICALLY writes something else - which you can say in the docs "do not attempt".
 Just calling toString() for the C functions isn't sufficient.
and the dip doesn't do that. That's why it passes the arguments unmodified - it is up to the function you are calling to do whatever it needs to do. Really, the C compatibility thing is a red herring. This dip is NOT actually compatible with printf out of the box; it silently does the wrong thing in almost ALL cases. What it does do is provide a hook for users that can be compatible with it. It does extremely little on its own.
Feb 03
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/20 6:54 PM, Adam D. Ruppe wrote:
 
 I'm dropping other stuff just cuz I don't want to argue down rabbit 
 holes. Let's see what common ground we can find and build upon that.
 
 On Sunday, 2 February 2020 at 20:13:36 UTC, Steven Schveighoffer wrote:
 I think we could get the best of both worlds if the interpolated 
 string itself was not just a string, but rather a library-defined type 
 (well something slightly more special -- it should implicitly cast to 
 a null-terminated immutable(char)* if needed, just like string literals).
Yes, indeed, I proposed this in the last round too. I think it is our best bet for a compromise to salvage this DIP. You would lose the methods on it ... however, we have UFCS, so not a deal breaker. Lots of the community is opposed to the requirement to import something for the UFCS flavor; that is a psychological barrier for many people. I'm not in love with that myself, but I can live with it.
Not too concerned. If you want actual strings from this feature, then you need to pay the cost of importing stuff from phobos. It also allows for someone to define their own less-heavy version.
 And I remain legit concerned about implicit conversions though:
 
 printf(i"$some_int");
 
 that looks OK, but SILENTLY DOES THE WRONG THING.  But if dmd warned on 
 the format specifier like gcc does, we're cool. Could be an enhancement 
 later. I warn about this but do not withhold support about it.
This doesn't concern me AT ALL. If you want printf to work, you need to understand printf and the problems it can have. writef is there, and works. I don't think the compiler has any business complaining about printf usage.
 The DIP mentions this one: `printf("%s $something", foo);` This also 
 does the wrong thing. But so does `printf(i"$something% complete");`, 
 since `% c` is a valid format specifier.... yet it doesn't look like 
 one, especially since the interpolated string use $ now.
 
 We need to make % go ahead and get translated to %% by the compiler so 
 the parsing function can still work sanely with it. That's the solution 
 to this. If the compiler makes % magic in the generated string, it needs 
 to encode % in the user input too.
I disagree. I don't want my SQL interpolated strings (which can use % for matching) to be tainted by the interpolation. Again, if you want to use string interpolation to call printf (or writef), you need to know what will happen, just like if you were calling it with string + args form.
 Then put a type on the format string, if it implicitly converts is up to 
 y'all.  I just need some way to detect the format string's presence via 
 in the type system for overloading existing functions.
I think this is the only thing we really agree on. Having a specialized type gives so much more options, and should decay into what is already proposed.
 
 Do those two small changes and this DIP will have my support.
 
 * specify that % in the user string gets translated to %% in the format 
 literal
 * put the format literal in a wrapper type.
 
 i"$foo %"
 
 is translated to the tuple:
 
 __d_format_literal!("%s %%"), foo
I'd do it a little different, so we don't throw away the work the compiler already did: i"$apples and ${%d}bananas" => (__d_format_literal!(Format.init, " and ", Format("%d")), apples, bananas) If there is an overload that takes whatever this returns, then this is used as the lowering. Otherwise, a string literal as specified by the DIP is used (or we have an alias this in the result to the string version).
 struct __d_format_literal(string fmt) {
          enum f = fmt;
          alias f this;
 }
 
 That implicitly converts and just works in printf. That's the answer.
This isn't much better than just passing the string, but still provides overload capability. However, this still means we have to parse things in the library if we want to do anything interesting.
 And we can actually add later the idea of making the interpolated 
 string a new type after this DIP is implemented,
As soon as this DIP is implemented, we're frozen. Any future change will face additional barriers as a breaking change. It is going to be a lot better to just do it at least somewhat correctly now.
I think we can do it in a way that's not a breaking change. Or at least doesn't break things that explicitly accept string format + args. We shouldn't be frozen with this. And of course, string interpolation may prove to leave things wanting, so there may be an appetite to update to something like this. -Steve
Feb 03
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 3 February 2020 at 14:37:22 UTC, Steven Schveighoffer 
wrote:
 I'd do it a little different, so we don't throw away the work 
 the compiler already did:

 i"$apples and ${%d}bananas"

 =>

 (__d_format_literal!(Format.init, " and ", Format("%d")), 
 apples, bananas)
Yes, that would be excellent. If you make a motion to amend the DIP, I'll withdraw my motion and second yours. Let's form a coalition and get Walter onboard! With this, there's no more magic %s from the compiler (though Format.init can and prolly should just return "%s") and there's no more need for % => %% since the library can reliably detect everything. I'd just note that `Format` here is more realistically `__d_format_item` or something, a new simple thingy rather than a complex phobos struct or whatever.
 If there is an overload that takes whatever this returns, then 
 this is used as the lowering. Otherwise, a string literal as 
 specified by the DIP is used (or we have an alias this in the 
 result to the string version).
yeah just alias this it. Let's not put too much magic in there when we already have a library solution with lowering.
Feb 03
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/3/20 9:52 AM, Adam D. Ruppe wrote:
 On Monday, 3 February 2020 at 14:37:22 UTC, Steven Schveighoffer wrote:
 I'd do it a little different, so we don't throw away the work the 
 compiler already did:

 i"$apples and ${%d}bananas"

 =>

 (__d_format_literal!(Format.init, " and ", Format("%d")), apples, 
 bananas)
Yes, that would be excellent. If you make a motion to amend the DIP, I'll withdraw my motion and second yours. Let's form a coalition and get Walter onboard!
I hope this can work, but I feel Walter might not be on board due to past comments from him. This is why I feel we can wait and get string interpolation in, and then later add this. But I'll throw the idea out there, and see what he says.
 
 With this, there's no more magic %s from the compiler (though 
 Format.init can and prolly should just return "%s") and there's no more 
 need for % => %% since the library can reliably detect everything.
Yeah, I like that too. For instance mysql can accept i"select * from sometable where id = $id" and not have to put the ${?} crap in front of it.
 I'd just note that `Format` here is more realistically `__d_format_item` 
 or something, a new simple thingy rather than a complex phobos struct or 
 whatever.
Right, the naming isn't important.
 If there is an overload that takes whatever this returns, then this is 
 used as the lowering. Otherwise, a string literal as specified by the 
 DIP is used (or we have an alias this in the result to the string 
 version).
yeah just alias this it. Let's not put too much magic in there when we already have a library solution with lowering.
I have 2 concerns here. First, I don't want to eagerly construct the string if not needed/used. But I can solve this by making toString a template function, which enums the constructed string if asked for. Second concern is that strings in general don't implicitly cast to immutable(char)*. Which means printf stops working. Of course, we can just enum the format string together, but then it's eagerly constructed. the "compiler magic" would alleviate both these concerns, which is why I suggested it. But if we did the enum with alias this, it would be I think fully compatible. -Steve
Feb 03
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/3/20 10:17 AM, Steven Schveighoffer wrote:
 On 2/3/20 9:52 AM, Adam D. Ruppe wrote:
 On Monday, 3 February 2020 at 14:37:22 UTC, Steven Schveighoffer wrote:
 I'd do it a little different, so we don't throw away the work the 
 compiler already did:

 i"$apples and ${%d}bananas"

 =>

 (__d_format_literal!(Format.init, " and ", Format("%d")), apples, 
 bananas)
Yes, that would be excellent. If you make a motion to amend the DIP, I'll withdraw my motion and second yours. Let's form a coalition and get Walter onboard!
I hope this can work, but I feel Walter might not be on board due to past comments from him. This is why I feel we can wait and get string interpolation in, and then later add this. But I'll throw the idea out there, and see what he says.
As I expected, this was rejected, and Walter didn't understand what I was saying, says we shouldn't bake % into the format specification (it doesn't), and that it's like AST macros (it's not). Since there's no arguing on the feedback thread, I didn't want to push it. -Steve
Feb 04
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven Schveighoffer 
wrote:
 As I expected, this was rejected, and Walter didn't understand 
 what I was saying, says we shouldn't bake % into the format 
 specification (it doesn't)
Yeah, this change actually lives up the DIP's own rationale while making advanced uses doable. Walter is wrong. We all need to stand united for these enhancements and not back down.
Feb 04
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
Due to the silly rules, I have to correct Walter's errors in this 
thread instead.

On Tuesday, 4 February 2020 at 05:41:34 UTC, Walter Bright wrote:
 On 2/3/2020 7:48 AM, Steven Schveighoffer wrote:
 The rationale for this change is twofold:
No rationale is given why this needs to be supported at all.
I suggest you read the post again. Moreover, consider this use case. We have an existing function Result query(T...)(string q, T args) {} The format of the `string q` here is to use `?n` to refer to `args[n-1]`. query("X > ?1", x); A user wants to try the new interpolated string on it. They try: query(i"X > $x"); It compiles! Then it launches nuclear missiles and self-destructs the computer. Turns out %s was the special code to `query` to indicate universal armageddon. (ok it'd probably just throw a syntax error exception BUT THERE IS NO WAY TO TELL.) I know what you're going to say. "the user needs to be aware of the string format". Well, there's no type system check so I *guarantee* you this is going to be a common problem. With a new type as an overload target, library authors can do something about it. Without it, well, I guess it means i'll get more irc posts and stack overflow points answering confused user's questions. But let's assume they do know. Do they have to consistently write: query(i"X > ${?1}(x)"); Of course, by this point the new feature has lost its appeal. You've made the wrong thing easy and the right thing hard. D, at its best, does the opposite. Or consider this: Window createWindow(string title, int width = 0, int height = 0) {} and the user writes createWindow(i"Window for $user_id on $pid") it compiles... but throws an invalid argument exception (or creates a randomly sized window). Weird. (the addendum would still allow this by default, which I think is a mistake, but am compromising for your desired for compatibility with printf. But at least with the addendum, it becomes possible to for the library author to add `Window createWindow(T...)(InterpolatedFormat fmt, T args) { static assert(0, "sorry i didnt implement interpolation here try doing .format on your string instead");` D has a type system for a reason. If anything, using it should be the default position and bypassing it requires special rationale! So: why are you opposed to using it?
 Note that the user can always write:

   writefln(i" ... $a ... ");
   writefln(i" ... $b ... ");
That does not do the same thing as `writefln(i" ... $a ... ", i" ... $b ... ")`. But even if it did, what about nesting? What happens if someone tries i"$(i"$foo") $bar" what happens? Under the current dip rules: ("%s %s", "%s", foo, bar) and you can't use that. It is impossible to tell where it was nested. (If you say "well, don't do that", I'd note the DIP makes no mention of it and does not prohibit it. So again, are we relying on user's voluntarily following best practices?) Of course, even with this addendum, printf wouldn't detect it. But just because not ALL errors will be detected doesn't mean we shouldn't detect MANY errors.
 The user can write writefln(i"$percentage%% complete") as 
 required.
Again, relying on users to consistently do something (except when calling functions that do it differently!) with no help from compile errors / library design.
 Baking % into the specification with special escapes completely 
 weds it to printf/writef formatting, which is carefully avoided 
 (at your request!).
The current DIP bakes % into the specification. The addendum moves this to the library for both increased flexibility AND increased detection of user errors (without losing the DIP's (minimal) printf compatibility, at your request!)
 It's having the language semantics defined by a hidden template
This is just creating a type. Programs do this all the time. foo("a") does something different than foo(1). foo("a"w) does something different than foo("a"). These are because they are all different types and foo is allowed to overload on the types of its parameters. And guess what? With the DIP as-is, foo(i"$a") does something different than foo(i"$b"). They construct different types already! All we're asking is that the format string itself also gets a new type for reliable detection in code. You need to justify why this is bad.
 (which happens nowhere else in D).
 [snip]
 The D compiler does such "lowering" to simpler forms in many 
 places already.
lol
Feb 04
next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Tuesday, 4 February 2020 at 14:33:59 UTC, Adam D. Ruppe wrote:
 [...] consider this:

    Window createWindow(string title, int width = 0, int height 
 = 0) {}

 and the user writes

    createWindow(i"Window for $user_id on $pid")
You made some good arguments about why structure is needed (versus having a string + a tuple), but this is a really good example. If anything the i"" should not return a string, but rather an InterpolatedString type (whatever that is). This not only allow overloading/static-if on it, but also the possibility to introspect it. For instance to extract the original string.
Feb 04
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 4 February 2020 at 14:33:59 UTC, Adam D. Ruppe wrote:
 [snip]
One thing I'm a little confused on with this proposal. Under DIP 1027, if I have a function that takes an string as input, then I believe it can take an interpolated string as input instead. Similarly, it works with printf. Under the addendum proposal, is it also the case that functions that take strings or printf can instead take the interpolated string? You have some comments about that it does have compatability with printf and there was some discussion about a toString method, but I wasn't 100% sure where that came down and why.
Feb 04
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/4/20 1:41 PM, jmh530 wrote:
 On Tuesday, 4 February 2020 at 14:33:59 UTC, Adam D. Ruppe wrote:
 [snip]
One thing I'm a little confused on with this proposal. Under DIP 1027, if I have a function that takes an string as input, then I believe it can take an interpolated string as input instead. Similarly, it works with printf. Under the addendum proposal, is it also the case that functions that take strings or printf can instead take the interpolated string? You have some comments about that it does have compatability with printf and there was some discussion about a toString method, but I wasn't 100% sure where that came down and why.
The intention is for __d_interpolatedString (or whatever) to return a struct that has an enum member which is the interpolated format string, which is then alias this'd to the struct. So the type should decay exactly into what the current DIP says. It should be 100% compatible with printf. But if you want to do something different with interpolated strings, you can overload your function to handle it specially. Once you do that, you have access to the original parsed interpolation string parts and formats. In short -- anything that would work with the DIP as currently written would work with this new proposal. -Steve
Feb 04
parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 4 February 2020 at 21:49:18 UTC, Steven Schveighoffer 
wrote:
 [snip]

 The intention is for __d_interpolatedString (or whatever) to 
 return a struct that has an enum member which is the 
 interpolated format string, which is then alias this'd to the 
 struct.

 So the type should decay exactly into what the current DIP 
 says. It should be 100% compatible with printf.

 But if you want to do something different with interpolated 
 strings, you can overload your function to handle it specially. 
 Once you do that, you have access to the original parsed 
 interpolation string parts and formats.

 In short -- anything that would work with the DIP as currently 
 written would work with this new proposal.

 -Steve
Thanks. I think my confusion was more related to Walter's DIP than your suggestion.
Feb 04
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 4 February 2020 at 18:41:20 UTC, jmh530 wrote:
 Under the addendum proposal, is it also the case that functions 
 that take strings or printf can instead take the interpolated 
 string?
That would depend on the druntime implementation. I think my preference would be it implicitly casts to string and const char* if and only if all format specifiers are given by the user. I just wrote up a revised DIP based on Walter's to detail my current thoughts: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f and wow that took like 3 hours. I have other things to do now so I haven't proofread it yet, but it basically explains what I'm feeling is ideal based on this thread with a couple small new ideas (like the `idup` overload that replaces my old `toString` proposal) But the executive summary is 1) Steven's proposal from earlier, but with `Format.init` changed to `Format(null)`. Same result now but easier to add another argument in the future if we decide to extend this. 2) A sample library implementation you can play with. You'll have to write out the lowering by hand though since I didn't do a mixin translator this time. 3) A couple use cases and benefit examples written up. I also want to mention i18n but I ran out of time like two hours ago so bleh. 4) Most Walter's limitations are eliminated and I cleaned up some of the language (like that MixinExpression) one too. I got more into the implementation weeds than I probably should have but with that you can see more clearly in exact detail what behavior I am envisioning.
Feb 04
next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Tuesday, 4 February 2020 at 23:03:45 UTC, Adam D. Ruppe wrote:
 On Tuesday, 4 February 2020 at 18:41:20 UTC, jmh530 wrote:
 [...]
That would depend on the druntime implementation. I think my preference would be it implicitly casts to string and const char* if and only if all format specifiers are given by the user. [...]
Thanks for doing this work Adam. Especially knowing that it will likely be dismissed by the leadership, with a sentence or two of explanation :) In my opinion, your DIP is the right way to do interpolated strings in D.
Feb 04
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 02:44:53 UTC, Jonathan Marler 
wrote:
 In my opinion, your DIP is the right way to do interpolated 
 strings in D.
so I put the author in there as "D community team effort" because it was your implementation of the tuple thing that changed my mind on interpolation being of any value to D at all, and IIRC you based that implementation on someone else's initial write up. And then of course in this thread, Steven changed my mind away from another idea and toward the core of what was here, and others too for a bunch of input. And yes, even Walter's DIP introducing the format string thing is actually a really solid idea too - the printf compatibility is legit cool just because we *can*, and this will also aid compatibility with things like gnu getline among others, and the format string wrapper enables `ref` parameters, which I'm still a little iffy on... but that's something none of my previous encapsulation ideas would have been able to do. This is really a very small tweak from what he had. So it is not *my* proposal. It is *our* proposal. We should all share in the credit... ...and all unite on insisting that leadership take our voices seriously.
Feb 04
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 4 February 2020 at 23:03:45 UTC, Adam D. Ruppe wrote:
 I just wrote up a revised DIP based on Walter's to detail my 
 current thoughts:

 https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
So I didn't explicitly say this in the document, but you can prove it by running my sample code void main() nogc { string name = "interpolation"; // I wrote out long form what // i"Hello, ${%s}(name.ptr)\n" // would be lowered to by hand..... printf(_d_interpolated_string!("Hello, ", _d_interpolated_format_spec("%s"), "\n")(), mixin("name.ptr")); } Observe how that *works* with nogc. Despite my specifically putting the format string builder inside a private method with an if(__ctfe) guard, called from a wrapper template to force a CTFE enum generating, it did not compile with -betterC, but this is a known bug in betterC, not a flaw with my proposal nor my sample implementation. See: https://issues.dlang.org/show_bug.cgi?id=19268 Of course, if we really wanted to, we could provide a separate implementation that works around the -betterC bug anyway, but even if we couldn't work around t, it would be ridiculous to cripple a new feature just because of a known bug in a niche dmd switch.
Feb 04
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/4/20 6:03 PM, Adam D. Ruppe wrote:
 On Tuesday, 4 February 2020 at 18:41:20 UTC, jmh530 wrote:
 Under the addendum proposal, is it also the case that functions that 
 take strings or printf can instead take the interpolated string?
That would depend on the druntime implementation. I think my preference would be it implicitly casts to string and const char* if and only if all format specifiers are given by the user. I just wrote up a revised DIP based on Walter's to detail my current thoughts: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
Thanks for writing this up. A few things: 0. You have missed one edit: * `'$' '{' FormatString '}' Argument`, then `_d_interpolated_format_spec(FormatString)` is written to the output string. Should say "is appended to the argument list" 1. I hate that this doesn't work: alias toFormatString!null this I did some investigation. The compiler treats enum'd strings as convertible to const char * in many cases but not all. For example: enum str = "hi\n"; enum str2 = "hello " ~ "world!\n"; enum str3 = text("hello", " world!\n"); printf(str); // ok printf(str2); // ok printf(str3); // nope IMO, this is a bug, no reason not to make this consistent. So I thought I would make a fancy mixin that generates the enum string from a tuple of strings using only concatenation. It works! But then I ran into another problem -- namely that alias this somehow removes this ability to auto-convert to const char *. This seems like another bug to me, and a limitation that we should fix. BTW, I really like that you give the library writer the ability to alter the default spec so easily. Writing small shims should be trivial. 2. I disagree that you shouldn't be able to do writefln(i"someint : $someint") without adding an overload to writefln. That is the one benefit of Walter's proposal that I like -- you can use it out of the box with existing functions. I know it means you have to be aware of the problems that can occur, and understand how the lowering actually works. But there are going to be quite a few functions that exist already with this kind of mechanism, and I don't want to make all those functions inaccessible except via adding overloads. But your CreateWindow example is also pretty compelling. It would be nice to make "send the first parameter as a string" opt-in. Is there a way we can do that without requiring overloads? 3. I'm also not sold on the "if they provided a format specifier, they know it returns a tuple" logic. Plenty of existing string interpolation libraries provide ways to format the parameters into strings, and still can be used as strings. see what other interpolation libraries do): if the interpolation is intended to be used as a string, it lowers to a call to String.Format. If its type is going to be IFormattable or FormattableString, it generates a structure not dissimilar to what we are wanting here [2]. So there is kind of a precedent for this feature in other string interpolation implementations. I'm not sure whether we can provide this level of seamlessness without return type overloads, but I like the idup idea, and I still think we shouldn't call this feature string interpolation, due to the existing "formattable tuple" is a good description. -Steve [1] https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated#compilation-of-interpolated-strings [2] https://docs.microsoft.com/en-us/dotnet/api/system.formattablestring?view=netframework-4.8
Feb 05
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 15:20:56 UTC, Steven 
Schveighoffer wrote:
 0. You have missed one edit:
thx, I'll come back to it later.
 1. I hate that this doesn't work:

 alias toFormatString!null this
Yeah, I assumed it would because an eponymous template to an enum string does work. But then you lose the ability to detect the new type (maybe if we had typedef LOL). But it doesn't... and you know that createWindow one actually really gets to me. I think that is likely to be a big problem with unintentional interactions confusing people. And, of course, other people's concerns about implicit GC contextual allocations are fair too. However, I agree it is likely a bug. That said... this is one of those trade-offs that could go either way anyway. Of course, it is purely in the library which in theory can be tweaked more easily in the future.
 2. I disagree that you shouldn't be able to do 
 writefln(i"someint : $someint")

 But your CreateWindow example is also pretty compelling.
Yeah, this is back to the both-sides-have-merit trade-off. I could probably go either way - we could default to permissive and handle more cleanly with another lib overload too (which we also talked about earlier in this thread). Or restrictive with helper functions as I'll get into next.
 It would be nice to make "send the first parameter as a string" 
 opt-in. Is there a way we can do that without requiring 
 overloads?
I can't think of a perfect way. Can't use a helper template on params (call(help!i"xx")) since that triggers "variable X is not available at compile time", and can't do a helper UFCS function (call(i"xx").help) since they cannot return a tuple. We could do `help!call(i"..")` though... opt-in at the call site on the function, not the args. (I'm using "help" cuz i can't think of a name right now but meh). So basically a template that makes a new overload right there. callWithFirstAsString!writefln(i"..."); and the implementation is auto help(alias fn, T...)(T t) { return fn(t[0].toFormatString!"%s", t[1 .. $]); } so that's easy enough. And of course you could provide other customization arguments in there like a different default formatter etc. So that could be our hook into existing functions. And of course that could be done for printf too instead of alias const char* this, but then we're talking an import so... again not perfect but plenty of good enough library options we can talk about or even develop independently once we get the core language rewrite in place. Interestingly a printf one could inspeect the types of t[1..$] to change the default specifier, e.g. if(is(typeof(t) == int) fmt ~= "%d" else static assert(0) and so on. So it could actually work quite nicely. Of course do that as a ctfe to enum so it isn't GC at runtime. (or failing that use a stack buffer.) It just needs that help!printf instead of printf. Which we can debate. (I actually would prefer to go that way but I'm trying to maintain Walter's printf desire too.) But lots of options. With zero changes to dmd itself once we get this change.
 3. I'm also not sold on the "if they provided a format 
 specifier, they know it returns a tuple" logic. Plenty of 
 existing string interpolation libraries provide ways to format 
 the parameters into strings, and still can be used as strings.
Yeah, I just figure if the user is specifying all the strings it means they must have at least thought about the format of those specifiers. They might be doing it for other purposes or just be wrong, but eh. Note the only change that did is enable `alias this` conversions; you can still ignore it and use the other facilities.

 it up to see what other interpolation libraries do)
creating an object :) Then we could implicitly convert to a single string with `alias toString this` or use the specialized type. but of course i think this tuple is a wee bit better since it lets us keep `ref` and `alias`. Might also be relevant with other storage classes like `scope`, `return`, etc. So that's the trade off there (with current D at least). I could go either way, but like I said in my other message with the list, I think the tuple's potential is indeed worth sacrificing the object's convenience (but idup and other functions 95% make up for that anyway!).

 description.
that would work.
Feb 05
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2020-02-05 16:20, Steven Schveighoffer wrote:


 see what other interpolation libraries do): if the interpolation is 
 intended to be used as a string, it lowers to a call to String.Format. 
 If its type is going to be IFormattable or FormattableString, it 
 generates a structure not dissimilar to what we are wanting here [2]. So 
 there is kind of a precedent for this feature in other string 
 interpolation implementations.
 
 I'm not sure whether we can provide this level of seamlessness without 
 return type overloads, but I like the idup idea
I've been thinking something similar for quite a while. Here's my idea, it's a simple extension to Adam's proposal: * If the string interpolation expression appears in the code as an argument to a function and if that function is declared with the stringInterpolation UDA it generates the struct according to Adam's proposal. Example: stringInterpolation string sql(T)(T) { ... } stringInterpolation string jsx(T)(T) { ... } float value; string table; sql(i"SELECT * FROM $table WHERE value = $value"); Lowered to: sql(_d_interpolated_string!(...)(...)); * In all other cases the compiler generates the struct according to Adam's proposal BUT also calls the `idup` helper function: auto a = "bar" auto b = 3; auto str = i"foo $a $b"; Lowered to: auto str = _d_interpolated_string!(...)(...).idup; static assert(is(typeof(str) == string)); assert(str == "foo bar 3"); I think this will be the most common usage, to just convert to a string like `std.format.format` would do. , and I still think we
 shouldn't call this feature string interpolation, due to the existing 

 "formattable tuple" is a good description.
Some version of "string build"? I.e. "string builder", "string building" or similar. -- /Jacob Carlborg
Feb 05
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 16:37:52 UTC, Jacob Carlborg 
wrote:
 I've been thinking something similar for quite a while. Here's 
 my idea, it's a simple extension to Adam's proposal:
Anything with an attribute makes me "bleh". I'd really rather just have the explicit function to build a string. The explicit function is not a big hassle while being more friendly to several other uses. I'm not convinced that assigning to a string variable will actually be the most common usage though. You're surely creating this string to do something... and the variable doesn't actually do anything. You're surely going to pass it to a function eventually.. and often sooner rather than later. I use `writefln` more often than `format`, for example. Most my cases of `"x" ~ to!string(y)` are in order to prepare a call to a function (actually most of those in my code are actually to make the `new Exception` argument - which I believe is bad code anyway, I'm just lazy af. I'd prolly just .idup an interpolated string there). I could be wrong... but I doubt it. And the `.idup` makes it so simple even if it is common I doubt it will be a problem in practice. If we put out a beta and it was a big problem though we could revisit.
 Some version of "string build"? I.e. "string builder", "string 
 building" or similar.
Yeah, I am thinking the formattable tuple name or maybe "string builder literal" too. did a few more minor tweaks here including the new name btw https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
Feb 05
next sibling parent reply Johannes Loher <johannes.loher fg4f.de> writes:
Am 05.02.20 um 23:30 schrieb Adam D. Ruppe:
 I'm not convinced that assigning to a string variable will actually be
 the most common usage though. You're surely creating this string to do
 something... and the variable doesn't actually do anything. You're
 surely going to pass it to a function eventually.. and often sooner
 rather than later.
A use case which I already had a few times already is templated strings for mixins. I also just noticed that for this particular case, there is another problem. Usually for such strings, I would use the q{} syntax. As far as I understand, the current proposal does not allow mixing those 2. Is that correct? What would the syntax be anyways if we allow mixing those 2? iq{}? Not being able to mix them would be very sad because this is the main use case where I have wished for string interpolation in the past.
Feb 05
next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Thursday, 6 February 2020 at 07:43:38 UTC, Johannes Loher 
wrote:
 I also just noticed that for this particular case, there is 
 another problem. Usually for such strings, I would use the q{} 
 syntax. As far as I understand, the current proposal does not 
 allow mixing those 2. Is that correct? What would the syntax be 
 anyways if we allow mixing those 2? iq{}?
using implicit concatenation: auto s = i""q{$bananas};
Feb 06
prev sibling parent Claude <claudemr live.fr> writes:
On Thursday, 6 February 2020 at 07:43:38 UTC, Johannes Loher 
wrote:
 Am 05.02.20 um 23:30 schrieb Adam D. Ruppe:
 I'm not convinced that assigning to a string variable will 
 actually be the most common usage though. You're surely 
 creating this string to do something... and the variable 
 doesn't actually do anything. You're surely going to pass it 
 to a function eventually.. and often sooner rather than later.
A use case which I already had a few times already is templated strings for mixins. I also just noticed that for this particular case, there is another problem. Usually for such strings, I would use the q{} syntax. As far as I understand, the current proposal does not allow mixing those 2. Is that correct? What would the syntax be anyways if we allow mixing those 2? iq{}? Not being able to mix them would be very sad because this is the main use case where I have wished for string interpolation in the past.
I also have the same use-case but - if I remember correctly - it's been said in that thread that it could be implemented later once we already have an working interpolated-string implementation. It is a reasonable answer to me.
Feb 06
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On Wednesday, 5 February 2020 at 22:30:14 UTC, Adam D. Ruppe 
wrote:

 Anything with an  attribute makes me "bleh". I'd really rather 
 just have the explicit function to build a string. The explicit 
 function is not a big hassle while being more friendly to 
 several other uses.
I disagree. This will make it feel more like a real string. I'm sure there will be complains and confusion that for some strings `.idup` is required and for some it's not.
 I'm not convinced that assigning to a string variable will 
 actually be the most common usage though. You're surely 
 creating this string to do something... and the variable 
 doesn't actually do anything. You're surely going to pass it to 
 a function eventually.. and often sooner rather than later.
It's not the actual assignment that I referred to. It's the how it's formatted. I don't think the most common use case would be to call a function like `sql` or `jsx`. I think it would be to format the string as `std.format.format` would do. Then if you assigne the string to a variable or pass it to a function that expects a `string` is less interesting.
 I use `writefln` more often than `format`, for example. Most my 
 cases of `"x" ~ to!string(y)` are in order to prepare a call to 
 a function (actually most of those in my code are actually to 
 make the `new Exception` argument - which I believe is bad code 
 anyway, I'm just lazy af. I'd prolly just .idup an interpolated 
 string there).
Yeah, exactly. But with my suggestion you would need the extra call to `.idup` and it would feel more like an actual string. Then that it goes through an extra struct will be an implementation detail in most case. -- /Jacob Carlborg
Feb 06
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 6 February 2020 at 10:53:18 UTC, Jacob Carlborg 
wrote:
 This will make it feel more like a real string.
It is not a real string. It is a string builder. They aren't supposed to feel the same. Just like how `[1,2,3].map!whatever` doesn't actually run any code until you force evaluation with foreach, each, array, or something. I know it doesn't work that way in other languages, but it DOES work that way in much D code and there's good reasons for it. That's my position and I'm sticking to it. so a bit more explanation, really the thing is that implicit conversion is directly at odds against other use cases. We'd either sacrifice a few cases entirely or sacrifice some in part and require compiler magic to even make it possible. Our options are: 1) Maintain my position. Conversion to string consistently requires an explicit call to `.idup` or another helper function. Everything else is possible with overloaded functions. Hits about 10 / 12 user desires. converts to string. This sacrifices some use cases entirely (readf in particular, possibly interacting poorly wit `scope`, `return`, etc attributes as well), but may be more familiar to users coming from other languages. Hits about 8 / 12 user desires 12 wishlist items, but at the cost of more compiler magic... Hitting 12/12 is impossible because one of those items is implicit conversion to string and another one of those items is NO implicit conversion to string (for various reasons)! So with those direct contradictions we just have to accept that 100% of desires will NOT be 100% achieved. My proposal gets us over 80% at a fraction of the cost of option
Feb 06
prev sibling parent Arredondo <arm.plus gmail.com> writes:
On Tuesday, 4 February 2020 at 23:03:45 UTC, Adam D. Ruppe wrote:
 I just wrote up a revised DIP based on Walter's to detail my 
 current thoughts:

 https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
This should be in the feedback thread too.
Feb 06
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven Schveighoffer 
wrote:
 [snip]

 As I expected, this was rejected, and Walter didn't understand 
 what I was saying, says we shouldn't bake % into the format 
 specification (it doesn't), and that it's like AST macros (it's 
 not). Since there's no arguing on the feedback thread, I didn't 
 want to push it.

 -Steve
Several people seem to think it is an important proposal, so I wouldn't want you to get discouraged. However, when you say "Walter didn't understand what I was saying", another way to look at it is "I didn't communicate this well enough to convince Walter".
Feb 04
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 05/02/2020 2:44 AM, jmh530 wrote:
 On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven Schveighoffer wrote:
 [snip]

 As I expected, this was rejected, and Walter didn't understand what I 
 was saying, says we shouldn't bake % into the format specification (it 
 doesn't), and that it's like AST macros (it's not). Since there's no 
 arguing on the feedback thread, I didn't want to push it.

 -Steve
Several people seem to think it is an important proposal, so I wouldn't want you to get discouraged. However, when you say "Walter didn't understand what I was saying", another way to look at it is "I didn't communicate this well enough to convince Walter".
We talked about this on IRC, our way of looking at it seems to have been that we expected Walter to ask questions instead of dismissing a potentially important suggestion. For the record, I do agree that it has potential to significantly improve the DIP and its use cases.
Feb 04
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/4/20 8:44 AM, jmh530 wrote:
 On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven Schveighoffer wrote:
 [snip]

 As I expected, this was rejected, and Walter didn't understand what I 
 was saying, says we shouldn't bake % into the format specification (it 
 doesn't), and that it's like AST macros (it's not). Since there's no 
 arguing on the feedback thread, I didn't want to push it.
Several people seem to think it is an important proposal, so I wouldn't want you to get discouraged.
I've been involved in many discussions like this with Walter, and it's not something I plan to do in this case. Once he makes up his mind, the chances that I can convince him otherwise shrink to almost non-existent, and it's not worth the effort IMO.
 However, when you say "Walter didn't 
 understand what I was saying", another way to look at it is "I didn't 
 communicate this well enough to convince Walter".
Possibly, but his response looks like he's trying to intentionally avoid discussing the proposal at all (most of the points were obviously incorrect, which leads me to believe that he would rather not discuss the actual proposal). I don't have lots of time to argue with people who aren't open to discussion. -Steve
Feb 04
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04.02.20 22:57, Steven Schveighoffer wrote:
 On 2/4/20 8:44 AM, jmh530 wrote:
 On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven Schveighoffer wrote:
 [snip]

 As I expected, this was rejected, and Walter didn't understand what I 
 was saying, says we shouldn't bake % into the format specification 
 (it doesn't), and that it's like AST macros (it's not). Since there's 
 no arguing on the feedback thread, I didn't want to push it.
Several people seem to think it is an important proposal, so I wouldn't want you to get discouraged.
I've been involved in many discussions like this with Walter, and it's not something I plan to do in this case. Once he makes up his mind, the chances that I can convince him otherwise shrink to almost non-existent, and it's not worth the effort IMO.
 However, when you say "Walter didn't understand what I was saying", 
 another way to look at it is "I didn't communicate this well enough to 
 convince Walter".
Possibly, but his response looks like he's trying to intentionally avoid discussing the proposal at all (most of the points were obviously incorrect, which leads me to believe that he would rather not discuss the actual proposal). I don't have lots of time to argue with people who aren't open to discussion. -Steve
+1. I have refrained from commenting on the string interpolation DIP so far because it is not important enough to me to yet again get into the situation that Steve, Adam, Jonathan,... are in now. It's a very frustrating way to lose a lot of what could have been productive time. Personally, I think it is ridiculous that the DIP focuses on printf over all other use cases so much yet in the common case, the proposed string interpolation will lead to access violations when used with printf in the most natural way, and it does not even support type safe formatting. This is not what people mean when they vote for string interpolation on a survey. jmh530: Have you read all the arguments made? Do you really think they are not compelling enough to at least warrant being addressed?
Feb 04
next sibling parent Johannes Loher <johannes.loher fg4f.de> writes:
On Tuesday, 4 February 2020 at 22:38:59 UTC, Timon Gehr wrote:
 Personally, I think it is ridiculous that the DIP focuses on 
 printf over all other use cases so much yet in the common case, 
 the proposed string interpolation will lead to access 
 violations when used with printf in the most natural way, and 
 it does not even support type safe formatting. This is not what 
 people mean when they vote for string interpolation on a survey.
+1
Feb 04
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 4 February 2020 at 22:38:59 UTC, Timon Gehr wrote:
 [snip]

  jmh530: Have you read all the arguments made? Do you really 
 think they are not compelling enough to at least warrant being 
 addressed?
I've read each version of the DIP and all the arguments. Admittedly, if I don't understand why something's important, I skim (and I did a bit of skimming on this topic as it's not super important to me). I think my confusion was more basic and related to the DIP itself, rather than the addendum. For instance, if I have a function void foo(string x) {} then I would have expected to be able to pass an interpolated string literal to it just as in any other. However, a closer reading of this DIP suggests that is not intended.
Feb 04
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 00:01:53 UTC, jmh530 wrote:
 For instance, if I have a function
 void foo(string x) {}
 then I would have expected to be able to pass an interpolated 
 string literal to it just as in any other. However, a closer 
 reading of this DIP suggests that is not intended.
Right, that will not work with any of the active proposals. Though with mine, you will get a type mismatch error if you try and you can put `.idup` on it to make a copy into a GC string. So it is still easy enough... while being possible to do *so* much more with it too. (and even with Walter's original one, you can import std.format; and then i"xxxx".format() to do it too.)
Feb 04
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2020 2:38 PM, Timon Gehr wrote:
 Personally, I think it is ridiculous that the DIP focuses on printf over all 
 other use cases so much yet in the common case, the proposed string 
 interpolation will lead to access violations when used with printf in the most 
 natural way, and it does not even support type safe formatting.
You seem to be arguing for both baked-in knowledge of printf format specifications, and against making it printf specific? Replying not just to Timon here: To clarify: 1. the DIP is not specific to printf, writef, or SQL formats. It has no knowledge of any of them. It isn't even specific to calling a function. It simply produces a tuple expression, the first of which is a format string constructed from user-specified (not builtin) formats. The only exception is using %s if the user neglects to give a specification. 2. printf-aware checking can be added, but that would (and should) be an entirely orthogonal proposal, and would be effective with or without interpolated strings, as it has literally nothing to do with this DIP. A possible printf-format-checker would be run *after* the lowering to the tuple expression, and so would be equally effective for using or not using interpolated strings. 3. having the interpolator escape % by writing %% means that % becomes "baked in" as a special character. This is a completely unnecessary complication. 4. I repeat that I only use printf as an example because it is so well known and I don't have to spend a lot of time explaining what printf is and how it works. Note that writefln is also included as an example. There is nothing at all printf or writef specific in the design. 5. This proposal is not at all intended to be a substitute for knowing how printf formatting works. You're still going to have to remember to use %d for printing integers rather than %s or %g. This alone suggests that the DIP is not focused on printf. ---- The DIP is minimalist. This is intentional, as it proposes a very simple and straightforward rewrite of a literal into a tuple expression. Understanding tuple expressions (an existing D feature) is essential to understanding the DIP. This is a technique called "lowering" and is a great way of simplifying the semantics of a language and reducing corner cases and implementation bugs. Other examples: 1. the compiler rewrites 'while' loops into 'for' loops 2. the compiler rewrites RAII constructions into `try-finally` constructions In fact, the compiler could profit from doing a lot more lowering transformations.
Feb 04
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Wednesday, 5 February 2020 at 06:07:21 UTC, Walter Bright 
wrote:
 The DIP is minimalist. This is intentional, as it proposes a 
 very simple and straightforward rewrite of a literal into a 
 tuple expression. Understanding tuple expressions (an existing 
 D feature) is essential to understanding the DIP. This is a 
 technique called "lowering" and is a great way of simplifying 
 the semantics of a language and reducing corner cases and 
 implementation bugs. Other examples:

 1. the compiler rewrites 'while' loops into 'for' loops

 2. the compiler rewrites RAII constructions into `try-finally` 
 constructions

 In fact, the compiler could profit from doing a lot more 
 lowering transformations.
I don't think the reason why people disagree with you is that they don't know what lowering is.
Feb 04
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2020 10:12 PM, FeepingCreature wrote:
 I don't think the reason why people disagree with you is that they don't know 
 what lowering is.
I've run into many who did not.
Feb 05
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 05/02/2020 9:57 PM, Walter Bright wrote:
 On 2/4/2020 10:12 PM, FeepingCreature wrote:
 I don't think the reason why people disagree with you is that they 
 don't know what lowering is.
I've run into many who did not.
The people you are replying to are regulars of the D community and have been around for 10+ years. They have learned from you. When you explain your decisions in depth, people listen. They study the problem. Every bodies priorities may be different, but that does not mean that their point is wrong or it is not backed up with the literature.
Feb 05
prev sibling next sibling parent reply Arine <arine123445128843 gmail.com> writes:
On Wednesday, 5 February 2020 at 06:07:21 UTC, Walter Bright 
wrote:
 To clarify:

 1. the DIP is not specific to printf, writef, or SQL formats. 
 It has no knowledge of any of them. It isn't even specific to 
 calling a function. It simply produces a tuple expression, the 
 first of which is a format string constructed from 
 user-specified (not builtin) formats.

 The only exception is using %s if the user neglects to give a 
 specification.
float a; double b; real c; int d; printf(i"$a $b $c $d"); So the above translates to: printf("%s %s %s %s", a, b, c, d); This DIP is worse than I thought... All this just so you can say it doesn't rely on a library implementation detail?
 2. printf-aware checking can be added, but that would (and 
 should) be an entirely orthogonal proposal, and would be 
 effective with or without interpolated strings, as it has 
 literally nothing to do with this DIP. A possible 
 printf-format-checker would be run *after* the lowering to the 
 tuple expression, and so would be equally effective for using 
 or not using interpolated strings.
Because of 1, I have to *COMPLETELY* disagree. There's no way in hell this DIP should move forward without a checker if you plan with functionality of 1. You're going to force people to label specifiers themselves, when the compiler knows full well what type they are passing? That's just rediculous. No other language does this. The whole point is to keep it clean where you can. A float should have a %f not a %s inserted into the format.
 3. having the interpolator escape % by writing %% means that % 
 becomes "baked in" as a special character. This is a completely 
 unnecessary complication.
It causes unexpected behavior. It wouldn't be that big of an issue if printf and friend's format was checked.
 4. I repeat that I only use printf as an example because it is 
 so well known and I don't have to spend a lot of time 
 explaining what printf is and how it works. Note that writefln 
 is also included as an example. There is nothing at all printf 
 or writef specific in the design.
Other than it uses '%s' and uses a format string with args. Something multiple people here have said they don't want. It is purposefully designed the way it is because of printf and friends. In the summarization of the DIP:
 the implementation must be compatible with BetterC, meaning 
 printf and similar C functions
printf is a driving force behind the implementation. If you could ignore printf this would be implemented completely differently.
 5. This proposal is not at all intended to be a substitute for 
 knowing how printf formatting works. You're still going to have 
 to remember to use %d for printing integers rather than %s or 
 %g. This alone suggests that the DIP is not focused on printf.
This is just so backwards. Why. Why not just use printf without interpolated strings at this point. This is what happens when someone that doesn't understand a feature and what benefits it brings tries to implement it. I'm sorry if that's harsh but it's the truth.
 ----

 The DIP is minimalist. This is intentional, as it proposes a 
 very simple and straightforward rewrite of a literal into a 
 tuple expression. Understanding tuple expressions (an existing 
 D feature) is essential to understanding the DIP. This is a 
 technique called "lowering" and is a great way of simplifying 
 the semantics of a language and reducing corner cases and 
 implementation bugs. Other examples:

 1. the compiler rewrites 'while' loops into 'for' loops

 2. the compiler rewrites RAII constructions into `try-finally` 
 constructions

 In fact, the compiler could profit from doing a lot more 
 lowering transformations.
The user doesn't have to interact with any of those. The user does have to interact with the result of an interpolated string as defined in this DIP. It isn't hidden from them. string v = i"value = $value"; A user isn't going to expect that to be: string v = ("value = %s", value); You are lowering it for one minor use case where it is convenient , and then causing ugly behavior everywhere else. Not to mention it doesn't even do it properly, cause if you want to use a float you have to manually put %f yourself.
Feb 04
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2020 10:48 PM, Arine wrote:
      float a;
      double b;
      real c;
      int d;
 
      printf(i"$a $b $c $d");
 
 So the above translates to:
 
      printf("%s %s %s %s", a, b, c, d);
That's right.
 Because of 1, I have to *COMPLETELY* disagree. There's no way in hell this DIP 
 should move forward without a checker if you plan with functionality of 1. 
 You're going to force people to label specifiers themselves, when the compiler 
 knows full well what type they are passing? That's just rediculous. No other 
 language does this. The whole point is to keep it clean where you can. A float 
 should have a %f not a %s inserted into the format.
The compiler actually cannot know that it is a printf format. For example, dmd is full of printf-like functions that are not called printf.
 You are lowering it for one minor use case where it is convenient , and then 
 causing ugly behavior everywhere else. Not to mention it doesn't even do it 
 properly, cause if you want to use a float you have to manually put %f
yourself.
If you don't care for printf, that's why D has writef, where %s means "just make it work".
 This is what happens when someone that doesn't understand a feature and what
 benefits it brings tries to implement it. I'm sorry if that's harsh but it's
 the truth.
Berating other people in the forum is not permitted. Professional demeanor is expected.
Feb 05
prev sibling parent reply Johannes Loher <johannes.loher fg4f.de> writes:
On Wednesday, 5 February 2020 at 06:48:43 UTC, Arine wrote:
     string v = i"value = $value";

 A user isn't going to expect that to be:

     string v = ("value = %s", value);
This still boggles my mind. I know that you can simply write string v = i"value = $value".format; instead but this is not what anybody who already knows about string interpolation from any other language will expect. It is really weird to have a string syntax (i"") where the result of such an expression is not actually a string (or at the very least implicitly convertible to one). Do we really want D to be the odd one out in this case?
Feb 05
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/5/20 4:15 AM, Johannes Loher wrote:
 On Wednesday, 5 February 2020 at 06:48:43 UTC, Arine wrote:
     string v = i"value = $value";

 A user isn't going to expect that to be:

     string v = ("value = %s", value);
This still boggles my mind. I know that you can simply write string v = i"value = $value".format; instead but this is not what anybody who already knows about string interpolation from any other language will expect. It is really weird to have a string syntax (i"") where the result of such an expression is not actually a string (or at the very least implicitly convertible to one). Do we really want D to be the odd one out in this case?
I suggested in the first review round [1] that we don't call this "string interpolation" to avoid this confusion (because everyone will just expect this). But I absolutely think that this is how D should support string interpolation. It's just so much more beneficial than translating to an automatic call to format (note that this WOULD require a library to do it, as the compiler can't possibly know how to string-ify all parameters). -Steve [1] https://forum.dlang.org/post/qt8ust$26if$1 digitalmars.com
Feb 05
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 09:15:07 UTC, Johannes Loher 
wrote:
 instead but this is not what anybody who already knows about 
 string interpolation from any other language will expect. It is 
 really weird to have a string syntax (i"") where the result of 
 such an expression is not actually a string (or at the very 
 least implicitly convertible to one).

 Do we really want D to be the odd one out in this case?
Yes. I understand this can be a pain and a bit surprising to new users - which is why my proposal special-cases the compiler error message to explain it - but in exchange for this inconvenience for that case, we gain a lot of benefits: 1) sql(i""); just works, with correct parameterization 2) jsx(i""); just works, with correct contextual encoding 3) writeln(i""); just works 4) printf(i""); can just work 5) Even readf(i""); just works! 6) Error detection is possible - at compile time - for all those scenarios 7) nogc i"".toBuffer(...) is possible. 8) gettext() style internationalization is possible, including with the D compiler itself collecting the strings! 9) foo!i"" works to collect D aliases so like `my_debug!i"$a and $b"` might give a = 5 and b = 3 10) whatever else library authors can dream up! All this while keeping it type-safe to detect improper or even just inefficient uses. And, of course, `string s = i"".idup;` is still a very convenient option when you want it, but with so many functions just working, you may want to use them instead of string assignment! Note that Javascript's template literals allow much of this stuff too and are really cool. I'm chatting with a user on IRC now who feels this is a dealbreaker to him, it MUST implicitly convert. But the next best pretty iffy. So I think `.idup` is an OK trade off here. Like Steven, I encourage you all to look at i"" not being an interpolated string per se, but being a "string builder literal" or something like that - it is syntax sugar that gives an entity you can get a string out of rather than a string itself. An interpolated string is just one of the *many* things you can build with this new literal, and I expect that once you use it with some cool library support, you'll agree the explicit `idup` in some cases - and that's all you have to do to get the plain GC string out of it, no import necessary for that case in my proposal - is worth the gains we get in all these other cases too.
Feb 05
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 06/02/2020 4:00 AM, Adam D. Ruppe wrote:
 1) sql(i""); just works, with correct parameterization
 2) jsx(i""); just works, with correct contextual encoding
 3) writeln(i""); just works
 4) printf(i""); can just work
 5) Even readf(i""); just works!
 6) Error detection is possible - at compile time - for all those scenarios
 7)  nogc i"".toBuffer(...) is possible.
 8) gettext() style internationalization is possible, including with the 
 D compiler itself collecting the strings!
 9) foo!i"" works to collect D aliases so like `my_debug!i"$a and $b"` 
 might give
    a = 5 and b = 3
 10) whatever else library authors can dream up!
Oh that is looking good. Now that is something I could use.
Feb 05
prev sibling parent reply Arine <arine123445128843 gmail.com> writes:
On Wednesday, 5 February 2020 at 15:00:31 UTC, Adam D. Ruppe 
wrote:
 1) sql(i""); just works, with correct parameterization
 2) jsx(i""); just works, with correct contextual encoding
 3) writeln(i""); just works
 4) printf(i""); can just work
 5) Even readf(i""); just works!
 6) Error detection is possible - at compile time - for all 
 those scenarios
 7)  nogc i"".toBuffer(...) is possible.
 8) gettext() style internationalization is possible, including 
 with the D compiler itself collecting the strings!
 9) foo!i"" works to collect D aliases so like `my_debug!i"$a 
 and $b"` might give
   a = 5 and b = 3
 10) whatever else library authors can dream up!

 All this while keeping it type-safe to detect improper or even 
 just inefficient uses.

 And, of course, `string s = i"".idup;` is still a very 
 convenient option when you want it, but with so many functions 
 just working, you may want to use them instead of string 
 assignment!

 Note that Javascript's template literals allow much of this 
 stuff too and are really cool.


 I'm chatting with a user on IRC now who feels this is a 
 dealbreaker to him, it MUST implicitly convert. But the next 

 there (which I argued earlier in this thread was worth it!), 


 off here.

 Like Steven, I encourage you all to look at i"" not being an 
 interpolated string per se, but being a "string builder 
 literal" or something like that - it is syntax sugar that gives 
 an entity you can get a string out of rather than a string 
 itself. An interpolated string is just one of the *many* things 
 you can build with this new literal, and I expect that once you 
 use it with some cool library support, you'll agree the 
 explicit `idup` in some cases - and that's all you have to do 
 to get the plain GC string out of it, no import necessary for 
 that case in my proposal - is worth the gains we get in all 
 these other cases too.
A better feature would be if you could just create your own function then. Cause having to put %d/f/etc... when you want to use printf is just backwards. C++ has a feature that lets users process strings, and they can customize how it gets interpreted. We can do something similar.
 1) sql(i""); just works, with correct parameterization
 2) jsx(i""); just works, with correct contextual encoding
 3) writeln(i""); just works
 4) printf(i""); can just work
 5) Even readf(i""); just works!
These don't "just work". You have to manually add the specifier based on what you are calling. If you can define your own interpolated string implementation, then it can. float value; string table; // current proposal, doesn't "just work": sql(i"SELECT * FROM $table WHERE value = $value"); sql("SELECT * FROM %s where value = %s", table, value); // error // now it works, but not "just": sql(i"SELECT * FROM ${?}table WHERE value = ${?}value"); sql("SELECT * FROM ? WHERE value = ?", table, value); // just works: sql(sql_i"SELECT * FROM $table WHERE value = $value"); sql("SELECT * FROM ? WHERE value = ?", table, value); // current proposal, doesn't "just work": printf(i"The $table and $value"); printf("The %s and %s", table, value); // error + unsafe // now it works, but not "just": printf(i"The $table and ${%d}value"); printf("The %s and %d", table, value); // ops, still "works" // ok now for real it works, but not "just" printf(i"The $table and ${%f}value"); printf("The %s and %f", table, value); // just works: printf(fmt_i"The $table and $value"); printf("The %s and %f", table, value); Otherwise if you have to manually specify what specifiers to use, then this is dead on arrival. -1 my vote for the proposal if you have to manual put specifiers, someone should put up a poll.
Feb 05
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 16:02:03 UTC, Arine wrote:
     // current proposal, doesn't "just work":
     sql(i"SELECT * FROM $table WHERE value = $value");
     sql("SELECT * FROM %s where value = %s", table, value); // 
 error
That's Walter's proposal. With *my* proposal, the decision for %s or ? or whatever is made by the library function (which is passed the knowledge it needs to know to make a smart decision) instead of by the language. So, for the end user, those things DO just work. Remember: I am NOT defending DIP 1027 as-is. I am defending our amendment to it/rewrite of it. See: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
Feb 05
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/5/20 1:07 AM, Walter Bright wrote:

 3. having the interpolator escape % by writing %% means that % becomes 
 "baked in" as a special character. This is a completely unnecessary 
 complication.
I want to respond to this point because I feel you have misunderstood the point of my rationale in the modification I proposed. I do NOT want the compiler (or the runtime) escaping anything. I want everything to go in as-is. However, the function which is called with this information CAN treat it differently if desired. For example (and this is just an example, not a requirement or expectation), we could pretty much make the `f` versions of write obsolete, as having the formatting done by string interpolation can simply be put into the non-f versions. Example: writeln(i"your hex data is ${%02x}someByte"); There's no need for the 'f' version of writeln there if the interpolated string format is not just a basic string, and recognized specially by writeln. Not only that, but if you did need to put interpolated strings later in the parameter list (which would now be possible), the "just slap a format at the end" is a horrible alternative, because it means you are allocating a string just to print stuff. This is providing more efficient code. AS A BENEFIT, in this instance, writeln can simply ignore any % that is in the string part of the format string in terms of a formatting specifier. This is so much cleaner, as 1) no matching of format specifiers to parameters need take place, the compiler has already done that work, and 2) the code actually doesn't have to parse the non-format parts of the format string AT ALL. In other words, I don't want the compiler or runtime knowing ANYTHING about formatting or the specific formatting specifiers required for formatting strings for any call (thank you for making that possible by passing the format specifier as-is). I just want it to be POSSIBLE to treat that differently than "all % in the first parameter are meaningful". I LIKE the DIP in that it gives you instant compatibility with existing format + args calls. That's its hallmark feature. I wouldn't want to change that, as instantly, you can start using it with all your code base with just cleaner-looking calls. But IMO we can and should do better to allow library writers the benefit of the compiler's knowledge.
 ----
 
 The DIP is minimalist. This is intentional, as it proposes a very simple 
 and straightforward rewrite of a literal into a tuple expression. 
 Understanding tuple expressions (an existing D feature) is essential to 
 understanding the DIP. This is a technique called "lowering" and is a 
 great way of simplifying the semantics of a language and reducing corner 
 cases and implementation bugs. Other examples:
 
 1. the compiler rewrites 'while' loops into 'for' loops
 
 2. the compiler rewrites RAII constructions into `try-finally` 
 constructions
 
 In fact, the compiler could profit from doing a lot more lowering 
 transformations.
The additional proposal isn't any less minimalist. All you are doing is transforming the format specification into a tuple instead of a string. I bet the code isn't even any more complicated. When I think about existing D features, to me the additional proposal is akin to the transition from opAdd, opMul, opSub, etc to opBinary!(string op). The power realized there can be realized here as well. The more things you give people to play with at compile time, the more efficient the code can be, the more specialized people can do things. Consider this -- we can do compile-time checking of format strings outside the compiler with this feature at any later time. Again, with minimal effort, as parsing the format string for valid % tokens isn't necessary. -Steve
Feb 05
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/5/2020 6:18 AM, Steven Schveighoffer wrote:
 All you are doing is 
 transforming the format specification into a tuple instead of a string.
What is the tuple generated for: i"your hex data is ${%02x}someByte" ?
Feb 07
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 7 February 2020 at 08:45:03 UTC, Walter Bright wrote:
 What is the tuple generated for:
read my document here: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
   i"your hex data is ${%02x}someByte"
(_d_interpolated_string!("your hex data is ", _d_interpolated_format_spec("%02x"))(), someByte) Note that is a tuple consisting of two members, one of which is a single template instantiation given two value arguments.
Feb 07
parent reply matheus <matheus gmail.com> writes:
On Friday, 7 February 2020 at 13:37:15 UTC, Adam D. Ruppe wrote:
 read my document here: 
 https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f
Adam I was reading your document and I have a question in an example where you wrote:
 // look at this clean new code :D
 auto window = createWindow(i"Process debugger $pid");
 It compiles without error. In bliss, the happy user thinks: "d 
 rox". Then... the program is run and the window width is 
 extraordinary. But the documentation says it will be 
 automatically sized by default. ... After some time, they 
 notice the window title has changed to ...
So for what I understood, the currently "DIP 1027" when calling: createWindow(i"Process debugger $pid") Will generate a string "with" comma, like: createWindow("Process debugger", $pid) So changing the size of the window according the value in $pid. Well if this is true, then this "DIP 1027" is not reasonable at all and really need some work. Matheus.
Feb 07
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 7 February 2020 at 13:53:27 UTC, matheus wrote:
 createWindow(i"Process debugger $pid")

 Will generate a string "with" comma, like:

 createWindow("Process debugger", $pid)
Almost, Walter's proposal hardcodes %s in the compiler so it would actually generate: createWindow("Process debugger %s", $pid)
 So changing the size of the window according the value in $pid.
but yes, indeed.
Feb 07
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 7 February 2020 at 13:56:54 UTC, Adam D. Ruppe wrote:
 createWindow("Process debugger %s", $pid)
oops no $ there of course. BTW I actually think % or something is a better choice like $ for the interpolation. I'm not really going to fight it since I don't care that much, but for people who want to use it for D code snippets just remember that a = a[1 .. $] is really common in D code... and that $ will have to be escaped here. No big deal just wanna point it out while it is on my mind.
Feb 07
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 7 February 2020 at 13:59:23 UTC, Adam D. Ruppe wrote:
 On Friday, 7 February 2020 at 13:56:54 UTC, Adam D. Ruppe wrote:
 createWindow("Process debugger %s", $pid)
oops no $ there of course. BTW I actually think % or something is a better choice like $ for the interpolation. I'm not really going to fight it since I don't care that much, but for people who want to use it for D code snippets just remember that
[snip] I recall Walter changing it to $ from something with % because that is what most other languages do.
Feb 07
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 7 February 2020 at 14:38:57 UTC, jmh530 wrote:
 I recall Walter changing it to $ from something with % because 
 that is what most other languages do.
Right. But those other languages aren't putting D code in their strings, which use $ as a fairly common symbol in array indexes. for its interpolation. It really isn't a big deal (I'm skeptical that interpolation will be of much value to mixins anyway), just something I want to make sure we're all aware of.
Feb 07
prev sibling parent reply Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Tuesday, 4 February 2020 at 22:38:59 UTC, Timon Gehr wrote:
 On 04.02.20 22:57, Steven Schveighoffer wrote:
 On 2/4/20 8:44 AM, jmh530 wrote:
 On Tuesday, 4 February 2020 at 12:47:28 UTC, Steven 
 Schveighoffer wrote:
 [snip]

 As I expected, this was rejected, and Walter didn't 
 understand what I was saying, says we shouldn't bake % into 
 the format specification (it doesn't), and that it's like 
 AST macros (it's not). Since there's no arguing on the 
 feedback thread, I didn't want to push it.
Several people seem to think it is an important proposal, so I wouldn't want you to get discouraged.
I've been involved in many discussions like this with Walter, and it's not something I plan to do in this case. Once he makes up his mind, the chances that I can convince him otherwise shrink to almost non-existent, and it's not worth the effort IMO.
 However, when you say "Walter didn't understand what I was 
 saying", another way to look at it is "I didn't communicate 
 this well enough to convince Walter".
Possibly, but his response looks like he's trying to intentionally avoid discussing the proposal at all (most of the points were obviously incorrect, which leads me to believe that he would rather not discuss the actual proposal). I don't have lots of time to argue with people who aren't open to discussion. -Steve
+1. I have refrained from commenting on the string interpolation DIP so far because it is not important enough to me to yet again get into the situation that Steve, Adam, Jonathan,... are in now. It's a very frustrating way to lose a lot of what could have been productive time. Personally, I think it is ridiculous that the DIP focuses on printf over all other use cases so much yet in the common case, the proposed string interpolation will lead to access violations when used with printf in the most natural way, and it does not even support type safe formatting. This is not what people mean when they vote for string interpolation on a survey. jmh530: Have you read all the arguments made? Do you really think they are not compelling enough to at least warrant being addressed?
The simple solution is just vote for not accepting the DIP ... personally, it will be my first DIP vote, and it will be against it. String interpolation is not solving anything that needs to be solved, as a consequence, I don't want another 'convenient' feature added if it's not super-easy to grasp and use. /P
Feb 05
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Wednesday, 5 February 2020 at 10:46:11 UTC, Paolo Invernizzi 
wrote:
 String interpolation is not solving anything that needs to be 
 solved, as a consequence, I don't want another 'convenient' 
 feature added if it's not super-easy to grasp and use.

 /P
I mean, define "needs to be solved"? Strictly speaking, once you have any Turing complete language, no language developed other is doing something that needs to be solved. What standard do you have for D feature development? String interpolation is something that many people wish D had. (See the survey.) Why is that insufficient?
Feb 05
parent Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Wednesday, 5 February 2020 at 11:15:35 UTC, FeepingCreature 
wrote:
 On Wednesday, 5 February 2020 at 10:46:11 UTC, Paolo Invernizzi 
 wrote:
 String interpolation is not solving anything that needs to be 
 solved, as a consequence, I don't want another 'convenient' 
 feature added if it's not super-easy to grasp and use.

 /P
I mean, define "needs to be solved"? Strictly speaking, once you have any Turing complete language, no language developed other is doing something that needs to be solved. What standard do you have for D feature development?
I don't think it's pertinent to this discussion thread, nor adding nothing, so I would not go dip in that.
 String interpolation is something that many people wish D had. 
 (See the survey.) Why is that insufficient?
Because it's suboptimal, and not trivial to grasp and use, like in other languages.
Feb 05
prev sibling next sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Wednesday, 5 February 2020 at 10:46:11 UTC, Paolo Invernizzi 
wrote:
 String interpolation is not solving anything that needs to be 
 solved
Readability is a moving target, and a subjective one as well. A lot programmers I have worked with find string interpolation to reduce the ugly-ness of their code, and I completely agree with them. To me that means it is a feature worth having. On top of that string interpolation can be used for DSL work, something D is well suited for, as well as for string mixins.
Feb 05
parent reply Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Wednesday, 5 February 2020 at 11:24:46 UTC, Sebastiaan Koppe 
wrote:
 On Wednesday, 5 February 2020 at 10:46:11 UTC, Paolo Invernizzi 
 wrote:
 String interpolation is not solving anything that needs to be 
 solved
Readability is a moving target, and a subjective one as well. A lot programmers I have worked with find string interpolation to reduce the ugly-ness of their code, and I completely agree with them. To me that means it is a feature worth having.
I agree with you, I use string interpolation all the time, in python, or javascript for example
 On top of that string interpolation can be used for DSL work, 
 something D is well suited for, as well as for string mixins.
That's fine, I'm doing SQL parsing in D, so definitely that would help. The all point is that, simply, IMHO, with that DIP in place, I would stick with business-as-usual, and go with the standard way of writing `bla %s foo %d`.format(bar, baz). A different beast is Adam / Steven proposal ... but to this one, my vote will be -1.
Feb 05
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Feb 05, 2020 at 11:38:58AM +0000, Paolo Invernizzi via Digitalmars-d
wrote:
[...]
 A different beast is Adam / Steven proposal ... but to this one, my
 vote will be -1.
[...] I agree. I would also vote -1 for this proposal. It's an anemic implementation of string interpolation that neither hits the target of what most people expect string interpolation to be, nor adds any new value that makes up for this lack. It's bringing a knife to a gunfight. Adam/Steven's proposal is *so* much better, in that it meets most people's expectations (the interpolated string implicitly convertible to string), *and* enables powerful use cases other languages can't even dream of (type-checked SQL generation, etc). It's bringing a gun to a knife fight. :-D T -- Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
Feb 05
next sibling parent Andre Pany <andre s-e-a-p.de> writes:
On Wednesday, 5 February 2020 at 11:51:00 UTC, H. S. Teoh wrote:
 On Wed, Feb 05, 2020 at 11:38:58AM +0000, Paolo Invernizzi via 
 Digitalmars-d wrote: [...]
 A different beast is Adam / Steven proposal ... but to this 
 one, my vote will be -1.
[...] I agree. I would also vote -1 for this proposal. It's an anemic implementation of string interpolation that neither hits the target of what most people expect string interpolation to be, nor adds any new value that makes up for this lack. It's bringing a knife to a gunfight. Adam/Steven's proposal is *so* much better, in that it meets most people's expectations (the interpolated string implicitly convertible to string), *and* enables powerful use cases other languages can't even dream of (type-checked SQL generation, etc). It's bringing a gun to a knife fight. :-D T
Exactly my opinion. I also vote -1 and hope we can get instead the proposal from Adam / Steven. Kind regards Andre
Feb 05
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 5 February 2020 at 11:51:00 UTC, H. S. Teoh wrote:
 the interpolated string implicitly convertible to string)
That's actually not true. It will not implicitly convert because it is still a tuple. The first element is a struct with `alias this`, but I still recommend AGAINST that implicitly converting to avoid the `createWindow` problem described here: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f#wrong-use-in-unrelated-function However, I did allow it to implicitly convert to `const(char)*` (actually it prolly should be immutable, but meh) for purposes like `printf`, but if and only if you specify formatters for every item in the string. See: https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f#on-implicit-conversions Instead of implicitly converting, you get a type error. I recommend the compiler special-case the type error to point users toward a web page explaining their options, one of which is an overload of `idup` in object.d https://gist.github.com/adamdruppe/a58f097d974b364ae1cbc8c050dd9a3f#diagnostics This, I feel, is the best compromise between our opposing wishes as a diverse community: 1) It prevents common mistakes while still allowing some flexibility with legacy functions. You still *can* call `printf` incorrectly, but the short, convenient syntax will not allow it. This means you are less likely to accidentally send it on a wild pointer chase. I personally think error checking is very important. D should not make the wrong thing easy. 2) It makes GC allocations explicit, yet convenient, with my proposed `idup` overload. If you just want to do string s = i"whatever"; it will error and then you just slap `.idup` at the end, same as if you were assigning from a const char[] without worrying about performance details. D users are no strangers to explicit extra calls for performance. See also: `.array` on ranges. People get used to it, then hopefully later learn why it does this and there can be better alternatives! But here to be friendly to new users, I do want the special error message in the compiler telling people what they can do and why via web link. We do special error messages for `writeln` import missing for the same reason. At the same time, D is supposed to be easy and many of us are perfectly OK with random GC allocations... and the .idup solution is close enough at very little - though admittably not zero - effort. 3) It makes other advanced uses range from very easy (if the library author utilizes the overload) to possible (user might need to specify strings or call other helper functions or write their own function depending on the exact circumstance).
Feb 05
parent nullptr <null p.tr> writes:
On Wednesday, 5 February 2020 at 13:26:13 UTC, Adam D. Ruppe 
wrote:
 This, I feel, is the best compromise between our opposing 
 wishes as a diverse community:
 [snip]
The implementation specified here is, more or less, what I would actually want out of string interpolation in D. The original DIP seems like it'd be way too easy to shoot yourself in the foot with if you tried to use it for anything other than printf/writefln.
Feb 05
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/5/20 5:46 AM, Paolo Invernizzi wrote:
 it will be my first DIP vote, and it will be against it.
I didn't think DIPs got voted on, I thought just W&A made a decision and that was it. Not that the community can't give feedback or affect those decisions, but there isn't a "voting" phase here: https://github.com/dlang/DIPs/blob/master/docs/process-authoring.md. -Steve
Feb 05
prev sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Monday, 3 February 2020 at 14:52:24 UTC, Adam D. Ruppe wrote:
 On Monday, 3 February 2020 at 14:37:22 UTC, Steven 
 Schveighoffer wrote:
 I'd do it a little different, so we don't throw away the work 
 the compiler already did:

 i"$apples and ${%d}bananas"

 =>

 (__d_format_literal!(Format.init, " and ", Format("%d")), 
 apples, bananas)
Yes, that would be excellent. If you make a motion to amend the DIP, I'll withdraw my motion and second yours. Let's form a coalition and get Walter onboard!
This looks really good. When using it as a DSL, there is no need to parse the '%s' the compiler just inserted. Plus it is a bit more structured than the bare tuple there was before. Best of both worlds, I would say.
Feb 03
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 03, 2020 at 07:10:54PM +0000, Sebastiaan Koppe via Digitalmars-d
wrote:
 On Monday, 3 February 2020 at 14:52:24 UTC, Adam D. Ruppe wrote:
 On Monday, 3 February 2020 at 14:37:22 UTC, Steven Schveighoffer wrote:
 I'd do it a little different, so we don't throw away the work the
 compiler already did:
 
 i"$apples and ${%d}bananas"
 
 =>
 
 (__d_format_literal!(Format.init, " and ", Format("%d")), apples,
 bananas)
Yes, that would be excellent. If you make a motion to amend the DIP, I'll withdraw my motion and second yours. Let's form a coalition and get Walter onboard!
This looks really good. When using it as a DSL, there is no need to parse the '%s' the compiler just inserted. Plus it is a bit more structured than the bare tuple there was before. Best of both worlds, I would say.
+1, this proposal trumps all the other ones I've seen so far. T -- Let X be the set not defined by this sentence...
Feb 03
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/2/2020 10:46 AM, Steven Schveighoffer wrote:
 This works just fine:
That's a good point. But it took me a moment to understand the `printf(5)` because what printf is is very deeply ingrained in me, hence I would not write code like that and wouldn't find it acceptable. It's jarring and misleading. (I used to use clever printf macros in C to alter printf's behavior, but eventually removed all that for the same reason.)
Feb 02
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/20 3:18 PM, Walter Bright wrote:
 On 2/2/2020 10:46 AM, Steven Schveighoffer wrote:
 This works just fine:
That's a good point. But it took me a moment to understand the `printf(5)` because what printf is is very deeply ingrained in me, hence I would not write code like that and wouldn't find it acceptable. It's jarring and misleading. (I used to use clever printf macros in C to alter printf's behavior, but eventually removed all that for the same reason.)
The printf(5) was just to show it can be overloaded. The real use case would be the interpolated string struct that everyone is discussing here (which you objected to), not adding integer printing ;). It's just that I can get that to work to demonstrate the overloadability of C functions. In essence: printf(i"I have $apples apples and $bananas bananas"); Works in both scenarios. With the DIP as-is (and no extra implementation in the library), and with the interpolated-string-to-struct idea (with a D-based shim as an overload). Not arguing against the DIP, or for the struct mechanism, but just against that objection. -Steve
Feb 02
prev sibling parent Lim <himeix outlook.com> writes:
In current DIP I found a thing called IdentifierExpression. I 
searched both discussion threads and D documentations, founded no 
result regarding what it is (if I didn't miss something).

Is it the same thing as C++ id-expression? Will there be any 
differences between the following versions of interpolation 
strings?

     string someString = "abc";

     auto versionA = i"$someString";
     auto versionB = i"$(someString)";
Feb 03
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 30 January 2020 at 09:46:38 UTC, Mike Parker wrote:
 This is the discussion thread for the Final Review of DIP 1027, 
 "String Interpolation":

 https://github.com/dlang/DIPs/blob/d8f2e769c3a8c711e7886ccecc93eac9795dae9c/DIPs/DIP1027.md

 This is the first review in which we are operating under the 
 new procedure of using separate threads for discussion and 
 feedback. Please see my blog post on the topic:

 https://dlang.org/blog/2020/01/26/dip-reviews-discussion-vs-feedback/

 Here in the discussion thread, you are free to discuss anything 
 and everything related to the DIP. Express your support or 
 opposition, debate alternatives, argue the merits... in other 
 words, business as usual.

 However, if you have any specific feedback for how to improve 
 the the proposal itself, then please post it in the feedback 
 thread. The feedback thread will be the source for the review 
 summary I write at the end of this review round. I will post a 
 link to that thread immediately following this post. Just be 
 sure to read and understand the Reviewer Guidelines before 
 posting there:

 https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md

 The review period will end at 11:59 PM ET on February 13, or 
 when I make a post declaring it complete. Discussion in this 
 thread may continue beyond that point.

 Please stay on topic here. I will delete posts that are 
 completely off topic.
The main issue I have with this DIP is that it performs one too many steps: Interpolated String > Strings and Expressions > Format String Some applications want the "Strings and Expressions" form rather than the "Format String" form. For example, my alterative D standard library that doesn't depend on libc https://github.com/dragon-lang/mar doesn't use format strings at all and I'd like to be able to use interpolated strings with it. This means that I'm going to need to reverse the last step to turn the resulting "Format String" back into a tuple of "Strings and Expressions". The problem is that it's easy to create a "Format String" from a tuple, but hard to reverse it, and by the time the compiler has performed this roundtrip conversion, compile times will start to suffer. I recall Andrei Alexandrescu talking about the importance of "composability" when referring to his memory allocation library. It's better to have many small components that perform one job that you can piece together, rather than a small number of big components that perform many jobs. In the latter case, you end up with complex interfaces and duplicate functionality scattered throughout. Interpolated Strings will be more powerful and flexible if we leave them in a "less processed form", where they can easily be used for more than string formatting functions.
Feb 03
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form rather than the 
 "Format String" form.
To have "strings and expressions" then write it that way: foo("I want ", i, " bananas");
Feb 03
next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Tuesday, 4 February 2020 at 07:12:11 UTC, Walter Bright wrote:
 On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form 
 rather than the "Format String" form.
To have "strings and expressions" then write it that way: foo("I want ", i, " bananas");
I explained why I thought it would be better for interpolated strings to resolve to "Strings and Expressions" rather than "Format Strings". Then you responded saying you can write "Strings and Expressions" without interpolated strings. However, I'm not sure what the relevance is. You can also write "Format Strings" without interpolated strings, like this: foo("I want %s bananas", bananas); But I'm not sure how these facts help us determine which form interpolated strings should resolve to?
Feb 04
prev sibling next sibling parent reply Arine <arine123445128843 gmail.com> writes:
On Tuesday, 4 February 2020 at 07:12:11 UTC, Walter Bright wrote:
 On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form 
 rather than the "Format String" form.
To have "strings and expressions" then write it that way: foo("I want ", i, " bananas");
This just seems entirely tone death to the comment he made. All interpolated strings are is syntactic sugar. You can just write the alternative (including printf), the purpose of interpolated strings is to not to. To be useful it has to become something like this:
 i"$apples and ${%d}bananas"

 =>

 (__d_format_literal!(Format.init, " and ", Format("%d")), 
 apples, bananas)
__d_format_literal is easy to parse compared to just a string. So it is easier to turn it back into essentially a tuple, at the cost of slow CTFE. With this it'd be easy to check and just throw an error at compile time if you don't want to allow custom formatting as well. This is probably the best solution so far that still allows customization and supports specifying custom formats that can be used with printf. i"value is ${%6.2f}value" I just wouldn't want Format() to just be a string either though. It'd be useful if it split all the options up appropriately. That way you don't have to parse the format string yourself.
Feb 04
parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 4 February 2020 at 18:20:27 UTC, Arine wrote:
 I just wouldn't want Format() to just be a string either 
 though. It'd be useful if it split all the options up 
 appropriately. That way you don't have to parse the format 
 string yourself.
You can use `std.format.FormatSpec` to parse the format string, if you don't want to write your own parsing code.
Feb 04
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/4/20 2:12 AM, Walter Bright wrote:
 On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form rather than 
 the "Format String" form.
To have "strings and expressions" then write it that way:     foo("I want ", i, " bananas");
To have "formatted string output", just write it that way: writefln("I have %s apples and %d bananas", apples, bananas); -Steve
Feb 04
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2020 1:59 PM, Steven Schveighoffer wrote:
 On 2/4/20 2:12 AM, Walter Bright wrote:
 On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form rather than the 
 "Format String" form.
To have "strings and expressions" then write it that way:      foo("I want ", i, " bananas");
To have "formatted string output", just write it that way: writefln("I have %s apples and %d bananas", apples, bananas);
The reason for formatted strings is for formatting that is other than the default, and to interface with common functions that use formatted strings. Strings and expressions are stuck with the default format. If you're happy with the default formatting, writefln("I want $i bananas"); is not much of any improvement over: writeln("I want ",i," bananas");
Feb 04
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Wednesday, 5 February 2020 at 05:26:30 UTC, Walter Bright 
wrote:
 The reason for formatted strings is for formatting that is 
 other than the default, and to interface with common functions 
 that use formatted strings.

 Strings and expressions are stuck with the default format. If 
 you're happy with the default formatting,

     writefln("I want $i bananas");

 is not much of any improvement over:

     writeln("I want ",i," bananas");
I think it's still a significant improvement. In my experience, anything that forces you to context switch from "inside string" to "outside string" is a frustrating source of typos and 'mental hiccups'. The point of format strings is that you can "stay inside" the string context and open a *new* expression context instead of mentally pre-flattening the string tree out into a list of fragments. (This is also a significant weakness of mixin strings.) In other words, the difference between ("I want $i bananas") and ("I want ", i, " bananas") is that one is (I want (i) bananas) and the other is ((I want ), (i), ( bananas)) - going down to nest vs going *up* to nest. This may sound trivial, but trivial syntax enhancements matter, especially for a "top-ten" feature like format strings.
Feb 04
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements matter, especially for
a 
 "top-ten" feature like format strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
Feb 05
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Wednesday, 5 February 2020 at 09:11:20 UTC, Walter Bright 
wrote:
 On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements 
 matter, especially for a "top-ten" feature like format strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
Okay, what actually is the cost of lowering to a template instantiation returning a type with an alias to a tuple, compared to lowering directly to a tuple? Is this significantly harder to implement and support?
Feb 05
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/5/2020 1:15 AM, FeepingCreature wrote:
 On Wednesday, 5 February 2020 at 09:11:20 UTC, Walter Bright wrote:
 On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements matter, especially 
 for a "top-ten" feature like format strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
Okay, what actually is the cost of lowering to a template instantiation returning a type with an alias to a tuple, compared to lowering directly to a tuple? Is this significantly harder to implement and support?
For one thing, it becomes part of the language specification. It has to be documented and taught. It turns a simple feature into a complex one. Having language semantics controlled by a hidden template is not in any other part of D. It implies that the behavior of interpolated strings is not predictable. Template instantiations are a LOT slower than direct implementation. Lots of interpolated strings means lots of template instantiations. Yes, it's also significantly more code to implement, and there has to be error recovery for when the template can't be found, and checks for a rogue template. All that has to be weighed against the benefit. Implementing something just because it is feasible and ticks a box is not a sufficient reason to do it. D must have smallish features that are widely useful, not be a large collection of trivia.
Feb 05
next sibling parent aliak <something something.com> writes:
On Thursday, 6 February 2020 at 07:00:33 UTC, Walter Bright wrote:
 sufficient reason to do it. D must have smallish features that 
 are widely useful, not be a large collection of trivia.
The irony 😃
Feb 06
prev sibling next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 6 February 2020 at 07:00:33 UTC, Walter Bright wrote:
 On 2/5/2020 1:15 AM, FeepingCreature wrote:
 On Wednesday, 5 February 2020 at 09:11:20 UTC, Walter Bright 
 wrote:
 On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements 
 matter, especially for a "top-ten" feature like format 
 strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
Okay, what actually is the cost of lowering to a template instantiation returning a type with an alias to a tuple, compared to lowering directly to a tuple? Is this significantly harder to implement and support?
For one thing, it becomes part of the language specification. It has to be documented and taught. It turns a simple feature into a complex one. Having language semantics controlled by a hidden template is not in any other part of D. It implies that the behavior of interpolated strings is not predictable. Template instantiations are a LOT slower than direct implementation. Lots of interpolated strings means lots of template instantiations.
Format strings force template instantiations anyways - even if you lower to a tuple. Consider the innards of format. Format strings *already* inescapably mean lots of template instantiations. One template instantiation on top of that isn't going to even move the needle. Furthermore, it actually improves the proposal by still allowing static type checking against format specifiers - which your tuple proposal does not allow, because the string is a runtime value. And yes, static format string checks cost templates, but this is something that everyone who works with format strings is already doing *anyways*. I'd rather D focuses its efforts on making templates fast rather than nursing a phobia against an integral, widely-used part of the language.
 Yes, it's also significantly more code to implement, and there 
 has to be error recovery for when the template can't be found, 
 and checks for a rogue template.
I don't understand the difficulty. Lookup should be module specific anyways? And I can't see error handling being more than a few lines to error out.
 All that has to be weighed against the benefit. Implementing 
 something just because it is feasible and ticks a box is not a 
 sufficient reason to do it. D must have smallish features that 
 are widely useful, not be a large collection of trivia.
D must have features that work as intuitively expected. The tuple proposal fails this test. Simplicity in a feature is not worth confusing the feature's users, especially when a moderate increase in complexity buys an order of magnitude increase in usefulness.
Feb 06
parent Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Thursday, 6 February 2020 at 12:30:20 UTC, FeepingCreature 
wrote:

  I'd rather D focuses its efforts on making templates fast 
 rather than nursing a phobia against an integral, widely-used 
 part of the language.
+1000
 D must have features that work as intuitively expected. The 
 tuple proposal fails this test. Simplicity in a feature is not 
 worth confusing the feature's users, especially when a moderate 
 increase in complexity buys an order of magnitude increase in 
 usefulness.
+1000, and underline "as intuitively expected"
Feb 06
prev sibling next sibling parent nullptr <null p.tr> writes:
On Thursday, 6 February 2020 at 07:00:33 UTC, Walter Bright wrote:
 For one thing, it becomes part of the language specification. 
 It has to be documented and taught. It turns a simple feature 
 into a complex one. Having language semantics controlled by a 
 hidden template is not in any other part of D. It implies that 
 the behavior of interpolated strings is not predictable. 
 Template instantiations are a LOT slower than direct 
 implementation. Lots of interpolated strings means lots of 
 template instantiations.

 Yes, it's also significantly more code to implement, and there 
 has to be error recovery for when the template can't be found, 
 and checks for a rogue template.

 All that has to be weighed against the benefit. Implementing 
 something just because it is feasible and ticks a box is not a 
 sufficient reason to do it. D must have smallish features that 
 are widely useful, not be a large collection of trivia.
If you aren't going to allow this feature to have enough complexity to be worthwhile then I'd suggest you retract this DIP all over the place because they Just Work and make string manipulation much easier to read. The implementation you've suggested here is the bare minimium to make string interpolation work in some cases for some things without any kind of protections for the user against accidentally (and silently!) breaking their code, and I would much rather not have string interpolation in D at all if the alternative is what is described in this DIP.
Feb 06
prev sibling next sibling parent Lim <himeix outlook.com> writes:
On Thursday, 6 February 2020 at 07:00:33 UTC, Walter Bright wrote:
 Template instantiations are a LOT slower than direct 
 implementation. Lots of interpolated strings means lots of 
 template instantiations.
For end users not familiar to D's new string interpolation solution. It could take a lot more time for them to figure out what just happened and to fix their code when something unexpected happened. This could be much more than the time to instantiate templates. I know this try and error will happen, hopefully, only once to each user but every compile would take time to do the template stuff. But the time to make functions written in the future fo correctly handle the DIP's implementation is also needs to be considered.
Feb 06
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/6/20 2:00 AM, Walter Bright wrote:
 On 2/5/2020 1:15 AM, FeepingCreature wrote:
 On Wednesday, 5 February 2020 at 09:11:20 UTC, Walter Bright wrote:
 On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements matter, 
 especially for a "top-ten" feature like format strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
Okay, what actually is the cost of lowering to a template instantiation returning a type with an alias to a tuple, compared to lowering directly to a tuple? Is this significantly harder to implement and support?
For one thing, it becomes part of the language specification. It has to be documented and taught. It turns a simple feature into a complex one. Having language semantics controlled by a hidden template is not in any other part of D. It implies that the behavior of interpolated strings is not predictable.
By that logic, associative arrays are not predictable, as most of the API for it is in object.d Neither is Object comparison (object.opCmp) This argument is bunk. If it's part of the spec, it's predictable, no matter if the compiler implements it or the library does.
 Template instantiations are a LOT slower than direct 
 implementation. Lots of interpolated strings means lots of template 
 instantiations.
This is a good point. I'll add also that every string interpolation would result in a new type (which has a cost). I would like for a different way for this to work, and would be OK probably with a non-templated struct. Obviously, the template version provides more flexibility and power. But a non-template type with e.g. an array of strings and an array of format specifiers (all in ROM) would be sufficient for most cases. You'd probably have to include the fully generated string for the implicit casting. One could also accept the interpolated string as a template parameter to get compile-time usage.
 Yes, it's also significantly more code to implement
It will be significantly more code for the library to implement if he has to parse the format string or add a special function to handle interpolation (because it doesn't follow the normal format for that domain, e.g. SQL).
 and there has to be 
 error recovery for when the template can't be found, and checks for a 
 rogue template.
These are solved problems. See all the templates inside the object module.
 
 All that has to be weighed against the benefit. Implementing something 
 just because it is feasible and ticks a box is not a sufficient reason 
 to do it. D must have smallish features that are widely useful, not be a 
 large collection of trivia.
 
This is a fundamental disconnect. It's not ticking a box, it provides real value as described many times across this thread. You are willfully ignoring those points. And it's why I almost want to delete this message, and focus on more important things. But I'll send this last one, and then you can approve your own DIP without listening to the community. -Steve
Feb 06
next sibling parent reply matheus <matheus gmail.com> writes:
On Thursday, 6 February 2020 at 16:03:54 UTC, Steven 
Schveighoffer wrote:
 But I'll send this last one, and then you can approve your own 
 DIP without listening to the community.

 -Steve
Wait a minute, can this happen? I mean someone approve own DIP like this? If yes, what's the reasoning of this discussion? Matheus.
Feb 06
next sibling parent nullptr <null p.tr> writes:
On Thursday, 6 February 2020 at 16:24:43 UTC, matheus wrote:
 On Thursday, 6 February 2020 at 16:03:54 UTC, Steven 
 Schveighoffer wrote:
 But I'll send this last one, and then you can approve your own 
 DIP without listening to the community.

 -Steve
Wait a minute, can this happen? I mean someone approve own DIP like this? If yes, what's the reasoning of this discussion? Matheus.
The idea is to refine a suggestion before it is sent to the language maintainers for approval. It falls apart, obviously, when the person making the suggestion is also the person doing the approval.
Feb 06
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Thursday, 6 February 2020 at 16:24:43 UTC, matheus wrote:
 On Thursday, 6 February 2020 at 16:03:54 UTC, Steven 
 Schveighoffer wrote:
 But I'll send this last one, and then you can approve your own 
 DIP without listening to the community.

 -Steve
Wait a minute, can this happen? I mean someone approve own DIP like this? If yes, what's the reasoning of this discussion? Matheus.
All DIPs require approval from Walter and Atila. That doesn't change because it's Walter's DIP. Some people have called this a conflict of interest, but this isn't a democracy and the ultimate decision for anything that goes into the language is up to Walter, although it'd be foolish not to take the community's opinion into account. The purpose of this discussion is to point out errors in the DIP and discuss it, IIRC.
Feb 06
parent matheus <matheus gmail.com> writes:
Answering both:

On Thursday, 6 February 2020 at 16:43:37 UTC, nullptr wrote:
 The idea is to refine a suggestion before it is sent to the 
 language maintainers for approval. It falls apart, obviously, 
 when the person making the suggestion is also the person doing 
 the approval.
On Thursday, 6 February 2020 at 17:03:42 UTC, Meta wrote:
 All DIPs require approval from Walter and Atila. That doesn't 
 change because it's Walter's DIP. Some people have called this 
 a conflict of interest, but this isn't a democracy and the 
 ultimate decision for anything that goes into the language is 
 up to Walter, although it'd be foolish not to take the 
 community's opinion into account.

 The purpose of this discussion is to point out errors in the 
 DIP and discuss it, IIRC.
Well I didn't know for me there was a sort of committee. I mean Walter could have a casting vote when tied, or more weight overall, but it's awkward seeing mostly only one person defining this. Anyway I should stop to not get this out of rails. Matheus.
Feb 06
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 6 February 2020 at 16:03:54 UTC, Steven 
Schveighoffer wrote:
 This is a good point. I'll add also that every string 
 interpolation would result in a new type (which has a cost).
A fairly small cost in exchange for a larger benefit (seamless compile time checking of format strings!). Template explosion in D *is* a problem in other places, but very unlikely to be here, because there'd be one template per builder, not n-squared or even n-factorial per builder argument list length as can easily happen with template metaprogramming and voldemort type mangling. So I doubt it would apply here anyway. And if it did, we can cross that when we come to it.
Feb 06
prev sibling parent Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 5 February 2020 at 09:11:20 UTC, Walter Bright 
wrote:
 On 2/4/2020 9:48 PM, FeepingCreature wrote:
 This may sound trivial, but trivial syntax enhancements 
 matter, especially for a "top-ten" feature like format strings.
It's a fair point, but there's a cost to supporting this. Nothing comes for free, and supporting everything is not practical.
I'd like to add that one of the reasons I utilize format strings is because of this switch. The main issue with foo("I want ", i, "bananas"); It is a lot harder to catch the spacing around 'i'. So rather than fix the space I also reorder everything to us format syntax. I'm on the side of leaning on D's power over C printf. I'd like to hear cost over the proposal too, maybe I missed it somewhere else?
Feb 05
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/5/20 12:26 AM, Walter Bright wrote:
 On 2/4/2020 1:59 PM, Steven Schveighoffer wrote:
 On 2/4/20 2:12 AM, Walter Bright wrote:
 On 2/3/2020 5:06 PM, Jonathan Marler wrote:
 Some applications want the "Strings and Expressions" form rather 
 than the "Format String" form.
To have "strings and expressions" then write it that way:      foo("I want ", i, " bananas");
To have "formatted string output", just write it that way: writefln("I have %s apples and %d bananas", apples, bananas);
The reason for formatted strings is for formatting that is other than the default, and to interface with common functions that use formatted strings.
In C, the reason for formatting string is because you don't know the types of what's passed in! There is no "default". We have adopted that mechanism in D because another nice feature is to be able to read the format of what you expect the result to be without all the comma-quote noise. But it's totally not necessary for formatting...
 
 Strings and expressions are stuck with the default format. If you're 
 happy with the default formatting,
 
      writefln("I want $i bananas");
 
 is not much of any improvement over:
 
      writeln("I want ",i," bananas");
 
They aren't stuck: writeln("I want ", someFloat.fmt("%0.2f"), " bananas"); We don't even need to change writeln to make this work today. But who would rather write that than a string interpolation tuple? The point of my original (admittedly snarky, sorry) reply to your rebuttal here is that your logic can be used to completely dismiss the whole string interpolation proposal. Why make the compiler more complicated when you can just "write it that way"? The answer is: because the other way is easier to write, easier to maintain, easier to read and overall improves the lives of developers everywhere. Syntax sugar is never necessary, but when it makes a difference it's a huge factor in the way it feels to use a language. I don't think we should dismiss features just because they can be written a different way in the existing language. -Steve
Feb 05
prev sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 5 February 2020 at 05:26:30 UTC, Walter Bright 
wrote:
 The reason for formatted strings is for formatting that is 
 other than the default, and to interface with common functions 
 that use formatted strings.
So the proposed design makes it impossible for a library to verify arguments are correctly corresponding to format specifiers, not even at runtime. The programmer is expected to know the format conventions of the function. Considering that, what is the purpose of putting the format specifier inside brackets {}? Take for example this format string using range formatting specifiers from Phobos: ``` byte[] arr = [0x10, 0x1A, 0x1F]; writefln("arr = ${%(%X; %)}arr"); // entire format specifier in {} writefln("arr = %(${%X}arr; %)"); // only element specifier in {} ``` The %( %) format specifiers are not the most beautiful to begin with, but the mandatory {} make it only worse. I would prefer to write: ``` writefln("The array is %(%s; %)$arr"); ``` It's not like the {} add any delimiting / error checking of any kind, so I might as well put the format specifier in the string myself. This would also be required if the format string itself contained unbalanced {} in them, since no means of escaping { is provided. But this still would not work with the current proposal, since a stray %s will be written into the string because of the absence of {}. Which, as mentioned before, is a direct contradiction with the design goal "the language does not now anything about format specifiers". One idea is to just write nothing instead of %s, and removing the {}, which would be less typing and remove the hard-wired Phobos convention from the language. Of course, with Steven/Adam's proposal, this can be made to work: ``` writefln("arr = ${X; }arr"); ``` Here the {} can actually do the delimiting instead of %( and %), and possibly do error checking as well!
 Strings and expressions are stuck with the default format. If 
 you're happy with the default formatting,

     writefln("I want $i bananas");

 is not much of any improvement over:

     writeln("I want ",i," bananas");
That second one is more prone to mistakes with whitespace, e.g.:
 text("a is", a, ", b is ", b, " and the sum is: ", a + b);
From: https://forum.dlang.org/post/jahvdekidbugougmyhgb forum.dlang.org
Feb 07
parent dweldon <danny.weldon gmail.com> writes:
On Friday, 7 February 2020 at 22:48:03 UTC, Dennis wrote:

 Considering that, what is the purpose of putting the format 
 specifier inside brackets {}?
Brackets are necessary to distinguish variables from other alpha-numeric characters. Eg. In shell (bash) programming, you can specify variables as $VARNAME or ${VARNAME}, the second form only necessary if the variable is adjacent to any alphanumeric characters. So, having said that, I would have thought that syntax like this would be better: 1. $variable 2. ${variable} 3. ${variable%d}, and eg. ${variable%02d} so that form 1 can be used when the variable is bounded by whitespace or punctuation but form 2 is needed to distinguish it from any adjacent alnum chars. Eg. "/tmp/prog${random}whatever" Form 3 would be for numeric type variables and allows optional formatting.
Feb 18
prev sibling parent FeepingCreature <feepingcreature gmail.com> writes:
Replying from review thread:

On Wednesday, 12 February 2020 at 11:49:11 UTC, Juraj Mojzis 
wrote:
 On Thursday, 30 January 2020 at 09:47:43 UTC, Mike Parker wrote:
 This is the feedback thread for DIP 1027, "String 
 Interpolation".
If writefln(i"I ate $apples and ${%d}bananas totalling $(apples + bananas) fruit."); gets rewritten as: writefln("I ate %s and %d totalling %s fruit.", apples, bananas, apples + bananas); Than for function: void my_err_log(string err_msg, string file, int line, int char) {...} called as: my_err_log(i"Error loading image $image_path.", src_file, src_line, src_char); gets rewritten as: my_err_log("Error loading image %s message", image_path, src_file, src_line, src_char); Results in an error but users will expect this to work. Worst case scenario is function that match: void draw_text(string text, int optional_x = 0, int optional_y = 0) {...} callad as: draw_text(i"Today is $day.$month."); For example, Phobos exceptions are mostly defined as : this(string msg, string file = null, size_t line = 0) this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) etc. In my opinion, this is the fundamental flaw in the proposal.
Code that users expect to work resulting in an understandable error is the second-best outcome there is. Since we don't have strong AI yet, given a situation that the compiler doesn't understand, this is pretty much the best result we can hope for. D has made a deliberate decision to eschew implicit conversions, so I don't think asking people to write my_error_log(i"Error loading image $image_path".format, src_file, src_line, src_char); instead is a big ask. Re draw_text(), this is a valid problem and more ammunition for the 'i"" should return a templated struct' proposal, which would neatly eliminate this issue.
Feb 12