www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Interpolated strings

reply Jonas Drewsen <nospam4321 hotmail.com> writes:
Hi all

   I've been wanting to have support for interpolated strings in D 
for some time now that will allow you to write e.g.:

auto a = 7;
writeln( $"{a} times 3 is {a*3}" );

Code speaks louder that words so I've made a PR that adds this 
support to ddmd as a RFC [1].

The compiler will basically lower the $"..." string to a mixin 
that concatenates
the expression parts of the (inside the {}) and the plain text 
parts.

I do recognize that this is not part of the 2017 H1 plan but I 
also believe such smaller improvements added regularly can make 
the language both more productive and attractive.

In case this addition gets a thumbs up I will of course improve 
test coverage and any needed quality of implementation.

[1] https://github.com/dlang/dmd/pull/6703
Apr 15
next sibling parent reply cym13 <cpicard openmailbox.org> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 Hi all

   I've been wanting to have support for interpolated strings in 
 D for some time now that will allow you to write e.g.:

 auto a = 7;
 writeln( $"{a} times 3 is {a*3}" );

 Code speaks louder that words so I've made a PR that adds this 
 support to ddmd as a RFC [1].

 The compiler will basically lower the $"..." string to a mixin 
 that concatenates
 the expression parts of the (inside the {}) and the plain text 
 parts.

 I do recognize that this is not part of the 2017 H1 plan but I 
 also believe such smaller improvements added regularly can make 
 the language both more productive and attractive.

 In case this addition gets a thumbs up I will of course improve 
 test coverage and any needed quality of implementation.

 [1] https://github.com/dlang/dmd/pull/6703
This has been proposed before, and I still don't see the added value compared to: auto a=7; writeln(a, " times 3 is ", a*3); besides adding compiler complexity, language complexity and parsing complexity which leaves room for more bugs to be invented. I'm a bit more harsh than I could be, but if this simple question has no clear answer I don't see why it sould make it into the language.
Apr 15
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 This has been proposed before, and I still don't see the added 
 value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
Apr 15
next sibling parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Saturday, 15 April 2017 at 20:20:49 UTC, Stanislav Blinov 
wrote:
 On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 This has been proposed before, and I still don't see the added 
 value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
Yes this is a very basic lowering. The value comes from the fact (assumption?) that this pattern of constructing a string from a mix of strings and expressions is very common. The value of a feature could be described as usage * productivity_improvement = feature_value So while the productivity_improvement may be modest the usage is high enough to give a high feature_value and justify the addition. A sign of this is the number of other popular languages supporting this feature (not arguing for just copy other languages but it is still an indicator). Unfortunately I have no hard data for these numbers.
Apr 15
parent reply cym13 <cpicard openmailbox.org> writes:
On Saturday, 15 April 2017 at 20:45:23 UTC, Jonas Drewsen wrote:
 On Saturday, 15 April 2017 at 20:20:49 UTC, Stanislav Blinov 
 wrote:
 On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 This has been proposed before, and I still don't see the 
 added value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
Yes this is a very basic lowering. The value comes from the fact (assumption?) that this pattern of constructing a string from a mix of strings and expressions is very common. The value of a feature could be described as usage * productivity_improvement = feature_value So while the productivity_improvement may be modest the usage is high enough to give a high feature_value and justify the addition. A sign of this is the number of other popular languages supporting this feature (not arguing for just copy other languages but it is still an indicator). Unfortunately I have no hard data for these numbers.
There is a very big hole in your equation. It's more like: feature_value = usage * productivity_improvement - (development_cost + maintainance cost) also it's worth noting that the productivity improvement is weighed down by the time needed to learn the new way, to understand other people's code that use yet another way to do things, to sift through code to find bugs... I'm not saying the end value isn't positive, maybe it is. I just don't see how right now. Just an anecdote: I often do security code reviews, that's my thing. I work with a lot of languages in that context, and I hate having to audit perl or ruby code. It's not because they're bad languages, but there is just so many ways to build strings and so many ways to execute shell code that finding them all in the code to check them is a nightmare. In python on the contrary there is a small set of functions that you can use for subprocessing. Besides they are all functions so even if they're different the structure is alike. This makes it *so* much easier. I want to be able to find bugs easily. I want D to be like python on that part. Not perl. Not ruby.
Apr 15
parent Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Sat, 2017-04-15 at 21:48 +0000, cym13 via Digitalmars-d wrote:
 [=E2=80=A6]
 structure is alike. This makes it *so* much easier. I want to be=C2=A0
 able to find bugs easily. I want D to be like python on that=C2=A0
 part. Not perl. Not ruby.
Python now has string interpolation, f-strings. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.n= et 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Apr 15
prev sibling parent reply cym13 <cpicard openmailbox.org> writes:
On Saturday, 15 April 2017 at 20:20:49 UTC, Stanislav Blinov 
wrote:
 On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 This has been proposed before, and I still don't see the added 
 value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
This tells me nothing. What value does it add really? Do we need yet another way to make a damn string? There is value in having a clear, unique interface. I know it's nowhere near unique right now but that only more reasons not to add yet another method to do what can already be done with 3 (!) more keystrokes.
Apr 15
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 15 April 2017 at 21:38:24 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:20:49 UTC, Stanislav Blinov 
 wrote:
 On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 This has been proposed before, and I still don't see the 
 added value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
This tells me nothing. What value does it add really? Do we need yet another way to make a damn string? There is value in having a clear, unique interface. I know it's nowhere near unique right now but that only more reasons not to add yet another method to do what can already be done with 3 (!) more keystrokes.
How about... it removes an import or two? Roughly 41.5% of Phobos (module-wise) depends on either format() or text(). And I didn't even include the ~ and .stringofs in the search. Quick visual scan suggests that a good portion of that could be replaced with the proposed solution, removing the dependency. Talk about "parsing complexity"... You have a peculiar way of asking for a clear answer. There are ways to vent frustration without inflicting it on others, you know.
Apr 15
next sibling parent reply cym13 <cpicard openmailbox.org> writes:
On Saturday, 15 April 2017 at 23:11:42 UTC, Stanislav Blinov 
wrote:
 On Saturday, 15 April 2017 at 21:38:24 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:20:49 UTC, Stanislav Blinov 
 wrote:
 On Saturday, 15 April 2017 at 20:12:41 UTC, cym13 wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 This has been proposed before, and I still don't see the 
 added value compared to:

     auto a=7;
     writeln(a, " times 3 is ", a*3);

 besides adding compiler complexity, language complexity and 
 parsing complexity which leaves room for more bugs to be 
 invented. I'm a bit more harsh than I could be, but if this 
 simple question has no clear answer I don't see why it sould 
 make it into the language.
Try a different context: auto a = 7; import std.format; auto str = format("%s times 3 is %s", a, a*3); //or import std.conv; auto str = text(a, " times 3 is ", a*3); //or auto str = $"{a} times 3 is {a*3}";
This tells me nothing. What value does it add really? Do we need yet another way to make a damn string? There is value in having a clear, unique interface. I know it's nowhere near unique right now but that only more reasons not to add yet another method to do what can already be done with 3 (!) more keystrokes.
How about... it removes an import or two? Roughly 41.5% of Phobos (module-wise) depends on either format() or text(). And I didn't even include the ~ and .stringofs in the search. Quick visual scan suggests that a good portion of that could be replaced with the proposed solution, removing the dependency. Talk about "parsing complexity"... You have a peculiar way of asking for a clear answer. There are ways to vent frustration without inflicting it on others, you know.
Removing imports is a good point, the first concrete one to be mentionned. I'm not sure it matters that much though as I think those imports are generic enough that I believe they would be imported anyway, but it's a real point. For parsing complexity I'm not talking about quantity actually, maybe I should say "parser complexity" instead. There again, all I want is a solid rational. This is far from being a little change, if the only things it brings are "looks nicer" and "one less import in some cases" I'm sure you'll agree that the case is pretty thin. It won't ever be my call but bear with me challenging the idea. I'm not venting frustration, and I'm not being insulting or anything, just clear and explicit with what problem I see.
Apr 15
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/15/2017 4:51 PM, cym13 wrote:
 Removing imports is a good point, the first concrete one to be mentionned. I'm
 not sure it matters that much though as I think those imports are generic
enough
 that I believe they would be imported anyway, but it's a real point.
It may not be necessary to have any dependencies on any import. $"{a} times 3 is {a*3}" could be rewritten by the parser to: "%s times 3 is %s", a, a * 3 and that is that. (I.e. just an AST rewrite.) It would be quite a bit simpler than Jonas' proposed implementation.
Apr 18
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-04-18 10:50, Walter Bright wrote:

 It may not be necessary to have any dependencies on any import.

     $"{a} times 3 is {a*3}"

 could be rewritten by the parser to:

     "%s times 3 is %s", a, a * 3

 and that is that. (I.e. just an AST rewrite.) It would be quite a bit
 simpler than Jonas' proposed implementation.
Then you would be forced to wrap it in a function call, to "format" or similar. The example of using "writeln" is not the best, a more useful example is when not printing the string immediately. For example: auto s = $"{a} times 3 is {a*3}"; With your example that would need to be written as: import std.format; auto s = format($"{a} times 3 is {a*3}"); -- /Jacob Carlborg
Apr 18
prev sibling next sibling parent Kagamin <spam here.lot> writes:
On Tuesday, 18 April 2017 at 08:50:07 UTC, Walter Bright wrote:
 It may not be necessary to have any dependencies on any import.

     $"{a} times 3 is {a*3}"

 could be rewritten by the parser to:

     "%s times 3 is %s", a, a * 3

 and that is that. (I.e. just an AST rewrite.) It would be quite 
 a bit simpler than Jonas' proposed implementation.
For, say, sql query generator it would be nice to have a rewrite like sql!"select name from users where id={n}" => auto q=query("select name from users where id= p0"); q.addParameter("p0",n); pragma(autoMixin) template sql(string txt) { enum string sql=generateCode(txt); } If this can be rewritten as automatic string mixin like so: sql!"select name from users where id={n}" => mixin(sql!"select name from users where id={n}")
Apr 18
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Tuesday, 18 April 2017 at 08:50:07 UTC, Walter Bright wrote:
 On 4/15/2017 4:51 PM, cym13 wrote:
 Removing imports is a good point, the first concrete one to be 
 mentionned. I'm
 not sure it matters that much though as I think those imports 
 are generic enough
 that I believe they would be imported anyway, but it's a real 
 point.
It may not be necessary to have any dependencies on any import. $"{a} times 3 is {a*3}" could be rewritten by the parser to: "%s times 3 is %s", a, a * 3 and that is that. (I.e. just an AST rewrite.) It would be quite a bit simpler than Jonas' proposed implementation.
I've thought about it and decided, I like this idea. I've only used interpolated strings in PHP which left a bad taste, but I realized that interpolating strings makes it impossible for your format string and your arguments to get out of sync. This makes interpolated strings harder to use incorrectly (Scott Meyers had a good talk on this, https://www.youtube.com/watch?v=5tg1ONG18H8). The biggest drawback I could think of was overhead, but with an implementation like this there is NO RUNTIME OVERHEAD! It's also simple (easy to implement and understand) and flexible (works with all existing functions that accept "format-like" arguments). Have you thought about supporting format specifiers as well? I looked at the C# version and it looks like they can specify them using a colon like this: $"{a} in hex is {a:x}" However this may create an ambiguity in the grammar, not sure. I'm not sure what kind of expression would be supported in the braces. If the expression supported labels then this would definitely cause ambiguity. In the example "{a:x}", the symbol "a" could just be parsed as a label.
Apr 18
next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Apr 18, 2017 at 09:56:28PM +0000, Jonathan Marler via Digitalmars-d
wrote:
[...]
 I've thought about it and decided, I like this idea.  I've only used
 interpolated strings in PHP which left a bad taste, but I realized
 that interpolating strings makes it impossible for your format string
 and your arguments to get out of sync. This makes interpolated strings
 harder to use incorrectly (Scott Meyers had a good talk on this,
 https://www.youtube.com/watch?v=5tg1ONG18H8).
[...] Phobos git HEAD now supports compile-time format strings of the form: writefln!"My %s value is %d."("field", 123); If your arguments are out of sync with the format string, this will abort with a compilation error. So you won't end up in the situation where the format string or arguments are wrong but you only find out when it throws a runtime exception. Or worse, in the C situation where mismatched format string / arguments can lead to stack overruns and security exploits. (Though C compilers have been hacked to check this and issue warnings. But that's veering offtopic.) Having said that, though, there is still one problem where you can still get into trouble: writefln!"The date is %04d-%02d-%02d"(month, year, day); It may not be immediately obvious that month and year have been transposed (presumably by mistake), because the %d specifier happens to work with both argument orderings. Whereas in an interpolated string, it becomes painfully obvious: // Using OP's tentative proposed syntax writeln($"The date is {month}-{year}-{day}"); // ^^^^^^^^^^^^^^ mistake is obvious here This problem may be especially prominent in D because we're used to writing %s as a general specifier that works for any argument type. T -- Long, long ago, the ancient Chinese invented a device that lets them see through walls. It was called the "window".
Apr 18
prev sibling next sibling parent Faux Amis <faux amis.com> writes:
On 2017-04-18 23:56, Jonathan Marler wrote:
 I've thought about it and decided, I like this idea.  I've only used 
 interpolated strings in PHP which left a bad taste, but I realized that 
 interpolating strings makes it impossible for your format string and 
 your arguments to get out of sync. This makes interpolated strings 
 harder to use incorrectly (Scott Meyers had a good talk on this, 
 https://www.youtube.com/watch?v=5tg1ONG18H8).
++ (ps. named arguments ;)
Apr 18
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/18/2017 2:56 PM, Jonathan Marler wrote:
 Have you thought about supporting format specifiers as well?  I looked at the
C#
 version and it looks like they can specify them using a colon like this:

     $"{a} in hex is {a:x}"
There are additional problems, such as: $"{a} in %s {b}" and positional parameters: $"{a} in {0}" Of course, the easiest solution is to just disallow that stuff.
Apr 18
next sibling parent Nick Treleaven <nick geany.org> writes:
On Wednesday, 19 April 2017 at 00:08:19 UTC, Walter Bright wrote:
 There are additional problems, such as:

     $"{a} in %s {b}"
% should be escaped: "%s in %%s %s". There would be no use for a single % otherwise.
 and positional parameters:

     $"{a} in {0}"
That would be literal 0: `"%s in %s", a, 0`. Could be disallowed but maybe not important. Presumably braces would be escaped $"\{ \}" -> "{ }". Also having no {code} block in an interpolated string should be an error. I like the simplicity of the lowering but it doesn't have much advantage over text(a, " in ", b), you still have to import a function. I suppose the advantage is readability for longer strings. Also, there are compile-time tricks to make formatting more efficient with a compile time format string - e.g. format!"%s in %s"(true, null). Here format can know the length of the resulting string. With your lowering format can't receive the format string as a compile-time argument.
Apr 19
prev sibling parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Wednesday, 19 April 2017 at 00:08:19 UTC, Walter Bright wrote:
 On 4/18/2017 2:56 PM, Jonathan Marler wrote:
 Have you thought about supporting format specifiers as well?  
 I looked at the C#
 version and it looks like they can specify them using a colon 
 like this:

     $"{a} in hex is {a:x}"
There are additional problems, such as: $"{a} in %s {b}" and positional parameters: $"{a} in {0}" Of course, the easiest solution is to just disallow that stuff.
What about supporting an optional prefix inside the {} like: int year = 2017; format($"The date is {%04d year}"); so if there is a % immediately following the { then the chars until next whitespace is format specifier. You can of course leave out the format specifier and it will default to %s.
Apr 19
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen wrote:

 What about supporting an optional prefix inside the {} like:

 int year = 2017;
 format($"The date is {%04d year}");

 so if there is a % immediately following the { then the chars 
 until next whitespace is format specifier. You can of course 
 leave out the format specifier and it will default to %s.
I really don't see how string interpolation is better then ` "The date is " ~ format("%04d", year)); `
Apr 19
next sibling parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen 
 wrote:

 What about supporting an optional prefix inside the {} like:

 int year = 2017;
 format($"The date is {%04d year}");

 so if there is a % immediately following the { then the chars 
 until next whitespace is format specifier. You can of course 
 leave out the format specifier and it will default to %s.
I really don't see how string interpolation is better then ` "The date is " ~ format("%04d", year)); `
As mentioned before it is only because it is such a common pattern that it justifies the change. Seems like many other languages reached that conclusion as well. Also take a look at a more realistic example with some more formatting and it will be more obvious (at least to me it is :) ) "The date is " ~ format("%04d", year)) ~ " and " ~ user ~ " just logged into " ~ here; $"The date is {%04d year} and {user} just logged into {here}"
Apr 19
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 19 April 2017 at 12:10:33 UTC, Jonas Drewsen wrote:
 On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen 
 wrote:

 What about supporting an optional prefix inside the {} like:

 int year = 2017;
 format($"The date is {%04d year}");

 so if there is a % immediately following the { then the chars 
 until next whitespace is format specifier. You can of course 
 leave out the format specifier and it will default to %s.
I really don't see how string interpolation is better then ` "The date is " ~ format("%04d", year)); `
As mentioned before it is only because it is such a common pattern that it justifies the change. Seems like many other languages reached that conclusion as well. Also take a look at a more realistic example with some more formatting and it will be more obvious (at least to me it is :) ) "The date is " ~ format("%04d", year)) ~ " and " ~ user ~ " just logged into " ~ here; $"The date is {%04d year} and {user} just logged into {here}"
I see. So you want to build format strings as well. This is going to be nasty, and likely to complex for a robust implementation. Here is what I would support: String interpolation literals can only be used with strings. And they need to start with some prefix which is not an operator. I"The date is %dateString and the time is %timeString"
Apr 19
parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Wednesday, 19 April 2017 at 14:02:43 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 12:10:33 UTC, Jonas Drewsen 
 wrote:
 On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen 
 wrote:

 What about supporting an optional prefix inside the {} like:

 int year = 2017;
 format($"The date is {%04d year}");

 so if there is a % immediately following the { then the 
 chars until next whitespace is format specifier. You can of 
 course leave out the format specifier and it will default to 
 %s.
I really don't see how string interpolation is better then ` "The date is " ~ format("%04d", year)); `
As mentioned before it is only because it is such a common pattern that it justifies the change. Seems like many other languages reached that conclusion as well. Also take a look at a more realistic example with some more formatting and it will be more obvious (at least to me it is :) ) "The date is " ~ format("%04d", year)) ~ " and " ~ user ~ " just logged into " ~ here; $"The date is {%04d year} and {user} just logged into {here}"
I see. So you want to build format strings as well. This is going to be nasty, and likely to complex for a robust implementation. Here is what I would support: String interpolation literals can only be used with strings. And they need to start with some prefix which is not an operator. I"The date is %dateString and the time is %timeString"
I'm talking about building format strings just yet... I'm just working with the suggestion that Walter brought up with converting the interpolated string into something that can be fed into format e.g.: $"The date is {%04d year} and {user} just logged into {here}" is rewritten by the compiler to: "The date is %04d and %s just logged into %s", year, user, here which can be fed into for example format(). Not sure I like the need to call format to get the resulting string, but just working with the idea here. I also think it would loose a lot of value to only allow strings as you suggest (e.g. %dateString).
Apr 19
parent Meta <jared771 gmail.com> writes:
On Wednesday, 19 April 2017 at 15:07:55 UTC, Jonas Drewsen wrote:
 I'm talking about building format strings just yet... I'm just 
 working with the suggestion that Walter brought up with 
 converting the interpolated string into something that can be 
 fed into format e.g.:

 $"The date is {%04d year} and {user} just logged into {here}"

 is rewritten by the compiler to:

 "The date is %04d and %s just logged into %s", year, user, here

 which can be fed into for example format(). Not sure I like the 
 need to call format to get the resulting string, but just 
 working with the idea here.

 I also think it would loose a lot of value to only allow 
 strings as you suggest (e.g. %dateString).
If we had language-level tuple literals you could desugar the expression into: ("The date is %04d and %s just logged into %s", year, user, here) And let a user do whatever they want with it. Even now it can be done with an AliasSeq, but not with the automatic insertion of format arguments, of course. alias fs = AliasSeq!("The date is %04d and %s just logged into %s", 1992, "Alan", "10.1.0.123"); writeln(format(fs)); //Prints "The date is 1992 and Alan just logged into 10.1.0.123"
Apr 19
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen 
 wrote:

 What about supporting an optional prefix inside the {} like:

 int year = 2017;
 format($"The date is {%04d year}");

 so if there is a % immediately following the { then the chars 
 until next whitespace is format specifier. You can of course 
 leave out the format specifier and it will default to %s.
I really don't see how string interpolation is better then ` "The date is " ~ format("%04d", year)); `
I can think of 3 reasons. 1. Requires GC. NOTE: I believe that most applcations should use GC memory, that being said, library code has to be nogc wherever it can if it wants to be used by nogc apps, so this solution is unusable in alot of library code. 2. It's verbose. Jonas provided a good example of why and he makes a good point that your example only has 1 formatted argument and this problem gets much worse with multiple. 3. The biggest reason I wouldn't use this solution because it uses string composition. String composition wastes memory and memory management cycles. And when I say "waste" what I mean is, the use is unnecessary. In general composition is a powerful tool because it allows you to easily overload and abstract and add new functionality, however, it requires runtime overhead. This is the reason that the toString(delegate) was created to replace the composable toString paradigm. It's also the reason that string formatting exists at all. To summarize: // requires GC, too verbose, uses string composition which wastes heap resources writeln("The date is " ~ format("%04d", year)); // efficient, but format string and args can get out of sync. writefln("the date is %04d", year); // writefln($"the date is {year:04d}"); If string interpolation gets reduced to the 2nd case, then it will have the same efficiency and solve a problem. Whether that problem justifies the change is up for debate, but I think it *might be*.
Apr 19
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Wednesday, 19 April 2017 at 13:04:08 UTC, Jonathan Marler 
wrote:
 On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen 
 wrote:
I can think of 3 reasons. 1. Requires GC. NOTE: I believe that most applcations should use GC memory, that being said, library code has to be nogc wherever it can if it wants to be used by nogc apps, so this solution is unusable in alot of library code. 2. It's verbose. Jonas provided a good example of why and he makes a good point that your example only has 1 formatted argument and this problem gets much worse with multiple. 3. The biggest reason I wouldn't use this solution because it uses string composition. String composition wastes memory and memory management cycles. And when I say "waste" what I mean is, the use is unnecessary. In general composition is a powerful tool because it allows you to easily overload and abstract and add new functionality, however, it requires runtime overhead. This is the reason that the toString(delegate) was created to replace the composable toString paradigm. It's also the reason that string formatting exists at all. To summarize: // requires GC, too verbose, uses string composition which wastes heap resources writeln("The date is " ~ format("%04d", year)); // efficient, but format string and args can get out of sync. writefln("the date is %04d", year); // writefln($"the date is {year:04d}"); If string interpolation gets reduced to the 2nd case, then it will have the same efficiency and solve a problem. Whether that problem justifies the change is up for debate, but I think it *might be*.
As a string interpolation sceptic I have to admit that I found one application of that concept that is probably much better than the current C derived format strings: Internationalisation. dates["en"] = "The date is %2$02d-%1$02d-%3$04d"; dates["fr"] = "La date est %1$02d-%2$02d-%3$04d"; writeln(format(dates[lan], day, month, year)); with interpolation we have dates["en"] = $"The date is {month:%02d}-{day:02d}-{year:04d}"; dates["fr"] = $"La date est {day:%02d}/{month:%02d}/{year:04d}"; which is a little bit easier to read than positional format specifiers, which have the added backdraw (at least in C, I don't know for D) that they are an none or ALL thing. When you need them, you have to put them an all format specifiers. This said, the interpolation has in that case a big drawback, i.e. that they "export" the variable names outside of the scope they are defined in. That's a thing that is often required for I10N, that the strings are defined in a separate file or module and selectively imported (at runtime).
Apr 19
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, Apr 19, 2017 at 06:38:13PM +0000, Patrick Schluter via Digitalmars-d
wrote:
[...]
 As a string interpolation sceptic I have to admit that I found one
 application of that concept that is probably much better than the
 current C derived format strings: Internationalisation.
 
     dates["en"] = "The date is %2$02d-%1$02d-%3$04d";
     dates["fr"] = "La date est %1$02d-%2$02d-%3$04d";
 
     writeln(format(dates[lan], day, month, year));
 
 with interpolation we have
 
     dates["en"] = $"The date is {month:%02d}-{day:02d}-{year:04d}";
     dates["fr"] = $"La date est {day:%02d}/{month:%02d}/{year:04d}";
 
 which is a little bit easier to read than positional format specifiers,
Somebody proposed some years ago to add named parameter support to format strings (if not to D itself). So ostensibly, instead of writing the nastiness like the above "La date est %1$02d...", you'd be able to write something like: dates["en"] = "The date is %{month}02d-%{day}02d-%{year}04d"; dates["fr"] = "La date est %{day}02d-%{month}02d-%{year}04d"; writefln(dates[lan], [ "day": day, "month": month, "year": year]);
 which have the added backdraw (at least in C, I don't know for D) that
 they are an none or ALL thing. When you need them, you have to put
 them an all format specifiers.
I think std.format.format actually allows you to mix positional and non-positional specifiers in the same format string. But the semantics have some tricky bits, though, so I wouldn't recommend doing it.
 This said, the interpolation has in that case a big drawback, i.e.
 that they "export" the variable names outside of the scope they are
 defined in. That's a thing that is often required for I10N, that the
 strings are defined in a separate file or module and selectively
 imported (at runtime).
With named parameter support in format strings, we wouldn't have this problem, since you could name the parameters however you want regardless of actual variable names. You could even perform arbitrary computations on the variables before passing it to format, e.g.: writefln("The date is %{year}04d-%{month}02d-%{day}02d", [ "year": yearsSinceEpoch + 1970, "month": zeroBasedMonth + 1, "day": zeroBasedDay + 1 ]); This way, implementation details like zero-based counting, years since the Unix Epoch, etc., are kept within the code where it belongs, not in the i18n strings. I'd argue that interpolated strings have a disadvantage here, because you wouldn't want to expose the computation `yearsSinceEpoch + 1970` to the l10n translators, who may inadvertently modify the expression to something wrong. Plus, allowing arbitrary (Turing-complete!) expressions inside l10n strings is just a security exploit waiting to happen. T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
Apr 19
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
 I really don't see how string interpolation is better then
 ` "The date is " ~ format("%04d", year)); `
That code is hideous, not hard to beat on every level... inefficient, hard to read. The built in thing could potentially optimize it a little too - maybe have it be syntax sugar over a range.
Apr 19
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/18/17 4:50 AM, Walter Bright wrote:
 On 4/15/2017 4:51 PM, cym13 wrote:
 Removing imports is a good point, the first concrete one to be
 mentionned. I'm
 not sure it matters that much though as I think those imports are
 generic enough
 that I believe they would be imported anyway, but it's a real point.
It may not be necessary to have any dependencies on any import. $"{a} times 3 is {a*3}" could be rewritten by the parser to: "%s times 3 is %s", a, a * 3 and that is that. (I.e. just an AST rewrite.) It would be quite a bit simpler than Jonas' proposed implementation.
Dmitry's solution is superior I think: $"{a} times 3 is {a * 3}" -> AliasSeq!(a, " times 3 is ", a * 3) Would work fine with writeln. Would work fine with format. Wouldn't be a drop-in replacement for a string, as other languages like to do. But has huge potential. I love Jonathan Marler's idea of using it for SQL inline grammar with prepared statements. Replacing something like: sql("INSERT INTO table VALUES (?, ?, ?, ?, ?, ?, ?, ?)", a, b, c, d, e, f, g, h); with: sql($"INSERT row INTO table VALUES ({a}, {b}, {c}, {d}, {e}, {f}, {g}, {h})"); That is absolutely awesome. What this does is match the parameter to where it belongs in the string, without all the extra quotes and commas. And it does no GC activity, and doesn't require any specific formatting tokens! -Steve
Apr 20
parent Nick Treleaven <nick geany.org> writes:
On Thursday, 20 April 2017 at 21:34:44 UTC, Steven Schveighoffer 
wrote:
 Dmitry's solution is superior I think:

 $"{a} times 3 is {a * 3}"

 ->

 AliasSeq!(a, " times 3 is ", a * 3)
+1, this is more flexible.
 Would work fine with writeln.
Yep, and std.conv.text. We might want a function that takes the quoted AliasSeq expansion and writes it to an output range.
 Would work fine with format.
No, std.format.format needs a format string as first argument, supplying extra arguments will not work. I like how this expansion *always* parses the string at compile-time. The expansion could be passed whole as template arguments if desired - I think this would allow text!(seq) which, if passed only literal strings and runtime scalar types, can return a fixed size static array, even though the scalar values are read and formatted at runtime.
Apr 21
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 15 April 2017 at 23:11:42 UTC, Stanislav Blinov 
wrote:
 How about... it removes an import or two?
It doesn't actually remove the dependency, it is just syntax sugar over it (there is precedent for this in the language, the pow operator calls a Phobos function, but it means you don't actually gain decoupling of modules).
Apr 15
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 15 April 2017 at 23:58:18 UTC, Adam D. Ruppe wrote:
 On Saturday, 15 April 2017 at 23:11:42 UTC, Stanislav Blinov 
 wrote:
 How about... it removes an import or two?
It doesn't actually remove the dependency, it is just syntax sugar over it (there is precedent for this in the language, the pow operator calls a Phobos function, but it means you don't actually gain decoupling of modules).
As presented, it doesn't. But it can be implemented in a way that it does. The compiler is capable of conversions without ever needing std.conv, and if custom .toString() is required, it will already be available anyway. As to the importance of this, I can only suggest measuring the code, both public and your own. Constructing a string out of variable values without any special formatting is, in my experience, a very common operation. Do importing a module *just* to build a mixin without building a mixin to build that mixin, static error message or a result for a script; CTFE evaluating a good deal of general-purpose code for a trivial use case have to be common as well?
Apr 15
parent Jonas Drewsen <nospam4321 hotmail.com> writes:
On Sunday, 16 April 2017 at 00:25:19 UTC, Stanislav Blinov wrote:
 On Saturday, 15 April 2017 at 23:58:18 UTC, Adam D. Ruppe wrote:
 On Saturday, 15 April 2017 at 23:11:42 UTC, Stanislav Blinov 
 wrote:
 How about... it removes an import or two?
It doesn't actually remove the dependency, it is just syntax sugar over it (there is precedent for this in the language, the pow operator calls a Phobos function, but it means you don't actually gain decoupling of modules).
As presented, it doesn't. But it can be implemented in a way that it does. The compiler is capable of conversions without ever needing std.conv, and if custom .toString() is required, it will already be available anyway.
I guess the phobos dependency could be removed with some extra work which I'll happily do. But the main worry I hear from people is the added language complexity which isn't going away.
Apr 17
prev sibling next sibling parent reply crimaniak <crimaniak gmail.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 The compiler will basically lower the $"..." string to a mixin 
 that concatenates
 the expression parts of the (inside the {}) and the plain text 
 parts.
It's easy implementable as a library (see https://github.com/Abscissa/scriptlike#string-interpolation) so it does not seem like a good idea to modify the language, only to change interp!"" to $"".
Apr 15
next sibling parent Jonas Drewsen <nospam4321 hotmail.com> writes:
On Saturday, 15 April 2017 at 20:35:56 UTC, crimaniak wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 The compiler will basically lower the $"..." string to a mixin 
 that concatenates
 the expression parts of the (inside the {}) and the plain text 
 parts.
It's easy implementable as a library (see https://github.com/Abscissa/scriptlike#string-interpolation) so it does not seem like a good idea to modify the language, only to change interp!"" to $"".
Lowerings are by definition possible to implement in a library. Using that argument foreach (i; 0..100) {} should not be part of the language even though I think very few would live without foreach.
Apr 15
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/15/2017 04:35 PM, crimaniak wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 The compiler will basically lower the $"..." string to a mixin that
 concatenates
 the expression parts of the (inside the {}) and the plain text parts.
It's easy implementable as a library (see https://github.com/Abscissa/scriptlike#string-interpolation) so it does not seem like a good idea to modify the language, only to change interp!"" to $"".
Yea, and note, I'm still open to the idea of better names than "interp". I'm still not entirely happy with that name. I'm even half-tempted to use "_". The only one problem I've found with doing it in library though: Far as I could tell, it seems to require the caller uses string mixins, which makes actually using it a little uglier and more verbose than I would like. Maybe I'm overlooking something obvious, but I haven't been able to find a way to change it to either a template mixin or even just a plain template without sacrificing to whole point of interpolated strings: specifying the arguments 100% inline. What I think would be ideal is a language enhancement to allow "interp" to do its job without the extra syntactical noise. That would not only give us good interpolates strings, but would likely have other applications as well.
Apr 16
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-04-16 18:10, Nick Sabalausky (Abscissa) wrote:

 What I think would be ideal is a language enhancement to allow "interp"
 to do its job without the extra syntactical noise. That would not only
 give us good interpolates strings, but would likely have other
 applications as well.
It's called AST macros ;) -- /Jacob Carlborg
Apr 17
prev sibling next sibling parent crimaniak <crimaniak gmail.com> writes:
On Sunday, 16 April 2017 at 16:10:15 UTC, Nick Sabalausky 
(Abscissa) wrote:
 Yea, and note, I'm still open to the idea of better names than 
 "interp". I'm still not entirely happy with that name. I'm even 
 half-tempted to use "_".
When I needed interpolation, I did not know about this library. So I create my own implementation and name it `expand`. I only needed class properties and did not support the expression, so this can not be considered a general solution, but name `expand` seems to be good for me. unittest { class InterTest { static int a = 1; static string bb = "--bb--"; mixin ExpandLocals; auto getInterpolated() { return expand!"a is $a, bb is $bb, and this is just $ sign"; } } assert((new InterTest).getInterpolated() == "a is 1, bb is --bb--, and this is just $ sign"); }
 What I think would be ideal is a language enhancement to allow 
 "interp" to do its job without the extra syntactical noise. 
 That would not only give us good interpolates strings, but 
 would likely have other applications as well.
Now I spend thinking some time about it and didn't find common solution without text mixin too.
Apr 17
prev sibling parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Sunday, 16 April 2017 at 16:10:15 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 04/15/2017 04:35 PM, crimaniak wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 The compiler will basically lower the $"..." string to a 
 mixin that
 concatenates
 the expression parts of the (inside the {}) and the plain 
 text parts.
It's easy implementable as a library (see https://github.com/Abscissa/scriptlike#string-interpolation) so it does not seem like a good idea to modify the language, only to change interp!"" to $"".
Yea, and note, I'm still open to the idea of better names than "interp". I'm still not entirely happy with that name. I'm even half-tempted to use "_". The only one problem I've found with doing it in library though: Far as I could tell, it seems to require the caller uses string mixins, which makes actually using it a little uglier and more verbose than I would like.
I was trying to get it shorter: // From // Output: The number 21 doubled is 42! int num = 21; writeln( mixin(interp!"The number ${num} doubled is ${num * 2}!") ); defining a new method exho! (derived from echo + mixin...:-) auto exho(string x)(){ return mixin("writeln("~interp!x~")");} You can just write: exho!"The number ${num} doubled is ${num * 2}!" This now looks more like "normal" scripting than writeln( mixin(interp!"The number ${num} doubled is ${num * 2}!") );
 Maybe I'm overlooking something obvious, but I haven't been 
 able to find a way to change it to either a template mixin or 
 even just a plain template without sacrificing to whole point 
 of interpolated strings: specifying the arguments 100% inline.

 What I think would be ideal is a language enhancement to allow 
 "interp" to do its job without the extra syntactical noise. 
 That would not only give us good interpolates strings, but 
 would likely have other applications as well.
I am not against the idea making string interpolations part of the language, in vibe.d diet templates the extrapolation is marked with #{var}, the ruby style, I like this too. At the beginning I thought this is already part of d. Regards mt.
Apr 17
parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Monday, 17 April 2017 at 19:12:37 UTC, Martin Tschierschke 
wrote:
 defining a new method exho! (derived from echo + mixin...:-)

   auto exho(string x)(){
      return mixin("writeln("~interp!x~")");}

 You can just write:

    exho!"The number ${num} doubled is ${num * 2}!"
It requires 'num' to be available to the exho function definition so will not work in the general case.
Apr 17
next sibling parent Martin Tschierschke <mt smartdolphin.de> writes:
On Monday, 17 April 2017 at 19:41:14 UTC, Jonas Drewsen wrote:
 On Monday, 17 April 2017 at 19:12:37 UTC, Martin Tschierschke 
 wrote:
 defining a new method exho! (derived from echo + mixin...:-)

   auto exho(string x)(){
      return mixin("writeln("~interp!x~")");}

 You can just write:

    exho!"The number ${num} doubled is ${num * 2}!"
It requires 'num' to be available to the exho function definition so will not work in the general case.
Thats a pity, so shouldn't we than try to find a way to define a alias like 'shortcut' for something like mixin(compiletime_expresion!var) so we can write just shortcut!var ? Or what about defining a new return type for functions allowing them to return a mixin even if defined in a lib? (Because the code above is allowed but only working if exho is defined in the same scope as the var 'num'.) Regards mt.
Apr 19
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/17/2017 03:41 PM, Jonas Drewsen wrote:
 On Monday, 17 April 2017 at 19:12:37 UTC, Martin Tschierschke wrote:
 defining a new method exho! (derived from echo + mixin...:-)

   auto exho(string x)(){
      return mixin("writeln("~interp!x~")");}

 You can just write:

    exho!"The number ${num} doubled is ${num * 2}!"
It requires 'num' to be available to the exho function definition so will not work in the general case.
Also, it only works if you're just sending the string to writeln. It doesn't help the general case :(
Apr 19
parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Wednesday, 19 April 2017 at 17:51:05 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 04/17/2017 03:41 PM, Jonas Drewsen wrote:
[...]
    exho!"The number ${num} doubled is ${num * 2}!"
Also, it only works if you're just sending the string to writeln. It doesn't help the general case :(
you can define: auto mixinter(string x)(){return mixin(interp!x);} (in the same scope as the vars used inside x) And use: string text = mixinter!"${name} and this are app ${age*365*24} hours!"; But can someone explain me why this works: import scriptlike; void main() { auto name = userInput!string("Please enter your name"); auto age = userInput!int("And your age"); // The proposed notation for scriplike string interpolation writeln(mixin(interp!"${name} you are app. ${age*365} days old")); // with exho definition auto exho(string x)(){return mixin("writeln("~interp!x~")");} exho!"${name} and this are app ${age*365*24} hours!"; } and this not? import scriptlike; // with exho definition outside the scope of the used vars auto exho(string x)(){return mixin("writeln("~interp!x~")");} void main() { auto name = userInput!string("Please enter your name"); auto age = userInput!int("And your age"); writeln(mixin(interp!"${name} you are app. ${age*365} days old")); exho!"${name} and this are app ${age*365*24} hours!"; } Can it be possible to allow a function to be defined outside the scope of use to return a "mixin object"?, than everything can be done in a lib outside, no need to add parsing complexity to the language?
Apr 20
next sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 20 April 2017 at 10:23:30 UTC, Martin Tschierschke 
wrote:
 Can it be possible to allow a function to be defined outside 
 the scope of use to return a "mixin object"?
That's basically what I propose: https://forum.dlang.org/post/msotbcaqipiiqxiuppnj forum.dlang.org this can be cheap compromise between macros and mixins.
Apr 20
parent reply Kagamin <spam here.lot> writes:
Also how various kinds of strings would work?
r$"{can}\i\has{slashes}"
$`same {here}`
$q{{balanced}braces}

For templates it's straightforward.
Apr 20
parent Nick Treleaven <nick geany.org> writes:
On Thursday, 20 April 2017 at 19:02:20 UTC, Kagamin wrote:
 Also how various kinds of strings would work?
 r$"{can}\i\has{slashes}"
 $`same {here}`
r"" and `` are WysiwygStrings. Interpolation is not WYSIWYG. $"" would need to support escaping of the interpolation start character, so may as well escape with backslash like "" strings.
 $q{{balanced}braces}
q{} strings have to lex correctly, so are intended for code - brace interpolation would play badly with braces in code. It might be useful with mixins if using a different interpolation syntax, e.g. $identifier or ${var+1} syntax within the string: void fun(string op)(T a, T b){ mixin(text($q{$a $op= $b;})); } (I used `text` for Dmitry's AliasSeq expansion - use `format` for Walter's).
Apr 21
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/20/2017 06:23 AM, Martin Tschierschke wrote:
 and this not?

 import scriptlike;

 // with exho definition outside the scope of the used vars

 auto exho(string x)(){return mixin("writeln("~interp!x~")");}

 void main()
 {

    auto name = userInput!string("Please enter your name");
    auto age = userInput!int("And your age");

    writeln(mixin(interp!"${name} you are app. ${age*365} days old"));

    exho!"${name} and this are app ${age*365*24} hours!";
 }
The definition for: exho!"${name} and this are app ${age*365*24} hours!" ...once the template is instantiated and the mixin is evaluated, is turned into this: auto exho() { return writeln("${name} and this are app ${age*365*24} hours!"); } But, there is no identifier "name" or "age" within the scope of exho. However, if exho is instead defined as a nested function WITHIN main, then as with any (non-static) nested function, it DOES have access to the local variables from the outer function. This, incidentally, is exactly why interp!"..." needs to be a string mixin in the first place.
Apr 20
parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/20/2017 02:40 PM, Nick Sabalausky (Abscissa) wrote:
 auto exho()
 {
      return writeln("${name} and this are app ${age*365*24} hours!");
 }
Correction: auto exho() { return writeln( ""~ _interp_text(name)~ " and this are app "~ _interp_text(age*365*24)~ " hours!" ); } Where _interp_text is really just a thin wrapper over std.conv.text. Again, note that `name` and `age` are undefined symbols unless exho is defined as a nested function within the function that actually defines `name` and `age`. And again, this is exactly why interp!"..." was forced to work by returning a string for the caller to mixin.
Apr 20
prev sibling next sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 ...
First, there's a process for language additions, please see https://github.com/dlang/DIPs/blob/master/README.md Secondly, I can tell you that any proposal that can be solved via the standard library has a very low chance of being accepted. D is already a complex language and it would take a important problem in order to justify making it more complex. As this is already do-able via Phobos, I would personally vote no to this addition.
Apr 15
parent Jonas Drewsen <nospam4321 hotmail.com> writes:
On Saturday, 15 April 2017 at 20:57:33 UTC, Jack Stouffer wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 ...
First, there's a process for language additions, please see https://github.com/dlang/DIPs/blob/master/README.md Secondly, I can tell you that any proposal that can be solved via the standard library has a very low chance of being accepted. D is already a complex language and it would take a important problem in order to justify making it more complex. As this is already do-able via Phobos, I would personally vote no to this addition.
Thank you. I have been following the forums for many years so am aware of the process and chances of something like this getting in. For that exact reason I wanted to do a minimal implementation to show that I'm serious and a RFC. Doing a DIP is a lot of work that I'm only willing to use my time on if I have some confidence that it will not be in vain. I will do the DIP if this ever gets a thumbs up.
Apr 15
prev sibling next sibling parent reply Xinok <xinok live.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 Hi all

 <snip>
I shared my thoughts on such a feature just a couple weeks ago: https://forum.dlang.org/post/oedeijdewmhazaqazdyo forum.dlang.org
Apr 15
parent Jonas Drewsen <nospam4321 hotmail.com> writes:
On Saturday, 15 April 2017 at 21:03:27 UTC, Xinok wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 Hi all

 <snip>
I shared my thoughts on such a feature just a couple weeks ago: https://forum.dlang.org/post/oedeijdewmhazaqazdyo forum.dlang.org
Most of you points applies to std.conv.text as well don't they? If you need special output buffers or memory management then use the tools right for that. This is about making the common case easier and keep the uncommon as it has always been.
Apr 15
prev sibling next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 4/15/17 10:04 PM, Jonas Drewsen wrote:
 Hi all

   I've been wanting to have support for interpolated strings in D for
 some time now that will allow you to write e.g.:

 auto a = 7;
 writeln( $"{a} times 3 is {a*3}" );

 Code speaks louder that words so I've made a PR that adds this support
 to ddmd as a RFC [1].

 The compiler will basically lower the $"..." string to a mixin that
 concatenates
 the expression parts of the (inside the {}) and the plain text parts.
Just a quick comment - I would be more interested if the string interpolation was extendable by the user. A simple path would be to make string interpolation produce AliasSeq of interleaved pieces of string with the arguments. So in your example writeln($"{a} times 3 is {a*3}") lowers to writeln(AliasSeq!(a, " times 3 is ", a*3)); which is basically writeln(a, " times 3 is ", a*3); but notice how depending on the function it can be used for other things such as DSLs.
 I do recognize that this is not part of the 2017 H1 plan but I also
 believe such smaller improvements added regularly can make the language
 both more productive and attractive.

 In case this addition gets a thumbs up I will of course improve test
 coverage and any needed quality of implementation.

 [1] https://github.com/dlang/dmd/pull/6703
--- Dmitry Olshansky
Apr 15
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-04-15 22:04, Jonas Drewsen wrote:
 Hi all

    I've been wanting to have support for interpolated strings in D for
 some time now that will allow you to write e.g.:

 auto a = 7;
 writeln( $"{a} times 3 is {a*3}" );

 Code speaks louder that words so I've made a PR that adds this support
 to ddmd as a RFC [1].

 The compiler will basically lower the $"..." string to a mixin that
 concatenates
 the expression parts of the (inside the {}) and the plain text parts.

 I do recognize that this is not part of the 2017 H1 plan but I also
 believe such smaller improvements added regularly can make the language
 both more productive and attractive.

 In case this addition gets a thumbs up I will of course improve test
 coverage and any needed quality of implementation.
My initial reaction is that this is something that can be implemented as library code if the language would have support for AST macros. On the other hand, this is something I would like to have in the language or the standard library. -- /Jacob Carlborg
Apr 16
parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Sunday, 16 April 2017 at 08:01:02 UTC, Jacob Carlborg wrote:
 On 2017-04-15 22:04, Jonas Drewsen wrote:
 [...]
My initial reaction is that this is something that can be implemented as library code if the language would have support for AST macros. On the other hand, this is something I would like to have in the language or the standard library.
I'm in favor of AST macros but I think that has been shot down by Walter already afaik.
Apr 17
parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Monday, April 17, 2017 18:10:23 Jonas Drewsen via Digitalmars-d wrote:
 On Sunday, 16 April 2017 at 08:01:02 UTC, Jacob Carlborg wrote:
 On 2017-04-15 22:04, Jonas Drewsen wrote:
 [...]
My initial reaction is that this is something that can be implemented as library code if the language would have support for AST macros. On the other hand, this is something I would like to have in the language or the standard library.
I'm in favor of AST macros but I think that has been shot down by Walter already afaik.
Walter is very much against AST macros and has said so on several occasions. It's one of those features that probably has about as much chance making it into the language as a snowball has in hell... - Jonathan M Davis
Apr 17
prev sibling next sibling parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 Hi all

   I've been wanting to have support for interpolated strings in 
 D for some time now that will allow you to write e.g.:

 auto a = 7;
 writeln( $"{a} times 3 is {a*3}" );

 Code speaks louder that words so I've made a PR that adds this 
 support to ddmd as a RFC [1].

 The compiler will basically lower the $"..." string to a mixin 
 that concatenates
 the expression parts of the (inside the {}) and the plain text 
 parts.

 I do recognize that this is not part of the 2017 H1 plan but I 
 also believe such smaller improvements added regularly can make 
 the language both more productive and attractive.

 In case this addition gets a thumbs up I will of course improve 
 test coverage and any needed quality of implementation.

 [1] https://github.com/dlang/dmd/pull/6703
Seems like there are mixed opinions about inclusion of a thing like this. Looking at all the old PRs on dlang github slowly bit rotting it makes me wonder how not to end up in that pool. I think that history has shown that Walter/Andrei are gatekeepers on what will ever get into the language. For the sake of contributers (incl. me or course :) ) it would make sense to get a preliminary "never going in" clarification by them early on. Even before a DIP has been written because writing that takes a good amount of effort. Also collect these declined language change decisions (such as AST Macros) on the wiki for future contributers to scan through before anything else. I volunteer to add "interpolated strings" to the page in case it gets declined. The page could also list pre-approved language changes such as async functions (which Walter wants afaik). I guess the main question is really to Walter and Andrei if we could get some kind "never going in" or "could be considered" clarification on e.g. news group threads named like e.g. "Language Design Change: Interpolated string"? Based on that the next steps: DIP, PR etc. step would take place.
Apr 17
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-04-17 21:28, Jonas Drewsen wrote:

 The page could also list pre-approved language
 changes such as async functions (which Walter wants afaik).
Another feature that can be implemented with AST macros. This is starting to get ridicules. So many features have been added and are talked about that can be implemented with AST macros instead. Same as with the scope/safe related DIPs. Many smallish specialized features are added instead of a generic feature that can handle all of them. Increasing the complexity of the language. -- /Jacob Carlborg
Apr 17
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 18 April 2017 at 06:54:11 UTC, Jacob Carlborg wrote:
 On 2017-04-17 21:28, Jonas Drewsen wrote:

 The page could also list pre-approved language
 changes such as async functions (which Walter wants afaik).
Another feature that can be implemented with AST macros. This is starting to get ridicules. So many features have been added and are talked about that can be implemented with AST macros instead. Same as with the scope/safe related DIPs. Many smallish specialized features are added instead of a generic feature that can handle all of them. Increasing the complexity of the language.
The corresponding ast-macros would be extremely complex, slow and worst of all not checkable.
Apr 17
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-04-18 08:59, Stefan Koch wrote:

 The corresponding ast-macros would be extremely complex
No, it's not that complex.
, slow and worst
 of all not checkable.
What do you mean "not checkable"? -- /Jacob Carlborg
Apr 18
parent reply bpr <brogoff gmail.com> writes:
On Tuesday, 18 April 2017 at 08:01:14 UTC, Jacob Carlborg wrote:
 On 2017-04-18 08:59, Stefan Koch wrote:

 The corresponding ast-macros would be extremely complex
No, it's not that complex.
Here's how it's done in Nim, a statically typed language similar to D, but with Python syntax, and macros. It takes some knowledge to understand, sure, macros are not a beginner tool, but wouldn't say this is extremely complex. I bet a D-with-macros would have a similar complexity solution. ------------------------------ string_interpolation.nim ------------------------------ import macros, parseutils, sequtils macro exho(text: string{lit}): untyped = var nodes: seq[NimNode] = [] # Parse string literal into "stuff". for k, v in text.strVal.interpolatedFragments: if k == ikStr or k == ikDollar: nodes.add(newLit(v)) else: nodes.add(parseExpr("$(" & v & ")")) # Fold individual nodes into a statement list. result = newNimNode(nnkStmtList).add( foldr(nodes, a.infix("&", b))) const multiplier = 3.14 message = exho"$multiplier times 2.5 is ${multiplier * 2.5}" foo = "foo" message2 = exho"$foo 3 times is ${foo & foo & foo}" echo message echo message2 -------------------------------------------------------------------------------- Running gives 3.14 times 2.5 is 7.850000000000001 foo 3 times is foofoofoo
Apr 18
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/18/2017 4:58 PM, bpr wrote:
 Here's how it's done in Nim, a statically typed language similar to D, but with
 Python syntax, and macros. It takes some knowledge to understand, sure, macros
 are not a beginner tool, but wouldn't say this is extremely complex. I bet a
 D-with-macros would have a similar complexity solution.
I'm not saying you cannot do cool and useful things with AST macros. My position is it encourages absolutely awful code as (usually inexperienced) programmers compete to show how clever their macros are. The language gets balkanized into a collection of dialects that are unrecognizable across user groups. As a compiler dev who gets stuck figuring out users' bug reports, dealing with templates is bad enough (the first thing I do with a bug report is redo it to remove all the templates). I do not want to deal with some custom syntax. If I may pull the "I'm older" card, programmers will find as they gain experience that the AST macros are just not worth it. This disastrous state of affairs has occurred with every language that supports macros. If you want a nauseous example, check out the Boost C preprocessor metaprogramming library. Or C++ expression templates - so cool, and yet so utterly wretched. Reminds me of a story from the 1980s. Microsoft's MASM stood for "Macro Assembler". Inevitably, Microsoft programmers invented a pile of macros that sort of turned asm programming into a pseudo-high-level language. This was shipped with every copy of MASM. A friend of mine who worked at MS was once given the task of fixing a bug in 50K of MASM code written in this macro language. The author of it had long since moved on. Every coder assigned to this task failed. My friend got it fixed in a couple hours. He was asked by his astonished manager how he'd managed to do it: "I assembled the code into an object file. Then I disassembled it with Zortech's OBJ2ASM, figured out how to fix it, then submitted the disassembled code as the new source code."
Apr 18
next sibling parent reply bpr <brogoff gmail.com> writes:
On Wednesday, 19 April 2017 at 00:30:31 UTC, Walter Bright wrote:
 I'm not saying you cannot do cool and useful things with AST 
 macros. My position is it encourages absolutely awful code as 
 (usually inexperienced) programmers compete to show how clever 
 their macros are.
I'd think that that's a problem with community coding standards.
 The language gets balkanized into a collection of dialects that 
 are unrecognizable across user groups.
I'm pretty sure that hasn't happened with every language that supports macros. Even in the case of Scheme, I don't think it's the macros that are responsible for all of the dialects. It's the fact that the core language never includes enough (no records, exceptions, modules, ...) so every group adds their own versions of these features. Maybe if macros didn't make that easier then Schemers would have added those things to the core, but that's a counterfactual that I don't find convincing.
 As a compiler dev who gets stuck figuring out users' bug 
 reports, dealing with templates is bad enough (the first thing 
 I do with a bug report is redo it to remove all the templates). 
 I do not want to deal with some custom syntax. If I may pull 
 the "I'm older" card, programmers will find as they gain 
 experience that the AST macros are just not worth it.
Some programmers will not find that. Others will find that other features you value are just not worth it. There are absolutely no categorical statements. :-)
 This disastrous state of affairs has occurred with every 
 language that supports macros.
I don't think I've ever heard from Common Lisp, Scheme or Clojure programmers that they'd like to remove macros from their respective languages for the reasons you mention. I don't see the disasters there. The Julia folks looked at the Lisp experience and decided to include macros. Both Rust and Nim support macros. Scala too. Not long enough for the disaster yet? It's certainly not all roses, and writing and debugging macros can be a PITA. I try to avoid them, and consider them a tool of last resort. But they're very powerful, and sometimes I'm not smart enough to figure out how to do what I want cleanly with less powerful features.
 If you want a nauseous example, check out the Boost C 
 preprocessor metaprogramming library. Or C++ expression 
 templates - so cool, and yet so utterly wretched.
Have you checked out examples of macros that are not so nauseating? I find Nim decent, and of course the Lisps have a long history of macrology. I think you're drawing a view of macros from cpp, and MASM, and such, and not so much from the Lisp family, or Nim. cpp macrology is very different! D is interesting to me mostly because of it's powerful templates and CTFE. It seems a shame (to me, obviously) that such a powerful static metaprogramming feature as macros will not be a part of D, but it's your language!
Apr 18
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 19 April 2017 at 03:49:09 UTC, bpr wrote:
 I don't think I've ever heard from Common Lisp, Scheme or 
 Clojure programmers that they'd like to remove macros from 
 their respective languages for the reasons you mention. I don't 
 see the disasters there. The Julia folks looked at the Lisp 
 experience and decided to include macros.
Lisp AST is minimal.
 Both Rust and Nim support macros. Scala too. Not long enough 
 for the disaster yet?
How many Rust programmers write their own macros? My impression is that Rust macros is a temporary fix because they don't have another meta programming scheme in place. I also believe that Rust macros can break between releases.
 last resort. But they're very powerful, and sometimes I'm not 
 smart enough to figure out how to do what I want cleanly with 
 less powerful features.
Like what? Certainly, term-rewrite-languages are powerful, e.g. the language Pure: https://en.wikipedia.org/wiki/Pure_(programming_language) But I don't quite see it as an important feature for an imperative language with an AST as complex as D and with rather non-uniform semantics. If you want AST-macros in D you should also argue for redefining the core language, and turn everything that is unnecessary and that can be done as lowering into macros (e.g. "for each").
Apr 18
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-04-19 08:51, Ola Fosheim Grøstad wrote:

 If you want AST-macros in D you should also argue for redefining the
 core language, and turn everything that is unnecessary and that can be
 done as lowering into macros (e.g. "for each").
If D had AST macros from the beginning, then yes, "foreach" could have been implemented as a macro. -- /Jacob Carlborg
Apr 19
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 19 April 2017 at 09:49:16 UTC, Jacob Carlborg wrote:
 On 2017-04-19 08:51, Ola Fosheim Grøstad wrote:

 If you want AST-macros in D you should also argue for 
 redefining the
 core language, and turn everything that is unnecessary and 
 that can be
 done as lowering into macros (e.g. "for each").
If D had AST macros from the beginning, then yes, "foreach" could have been implemented as a macro.
Yup. And actually also "while" and "for". More minimal languages just have: block, conditional and jump-to-start-of-block.
Apr 19
parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 19 April 2017 at 16:19:09 UTC, Ola Fosheim Grøstad 
wrote:
 Yup. And actually also "while" and "for". More minimal 
 languages just have: block, conditional and 
 jump-to-start-of-block.
This reminds me of Rust's mid-level IR for some reason. For instance, according to one of the Rust blog posts goto completely replaces loop, break, and continue. Does it make sense to think of MIR as AST macros that only the compiler has access to?
Apr 19
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-04-19 02:30, Walter Bright wrote:

 I'm not saying you cannot do cool and useful things with AST macros. My
 position is it encourages absolutely awful code as (usually
 inexperienced) programmers compete to show how clever their macros are.

 The language gets balkanized into a collection of dialects that are
 unrecognizable across user groups.

 As a compiler dev who gets stuck figuring out users' bug reports,
 dealing with templates is bad enough (the first thing I do with a bug
 report is redo it to remove all the templates). I do not want to deal
 with some custom syntax. If I may pull the "I'm older" card, programmers
 will find as they gain experience that the AST macros are just not worth
 it.

 This disastrous state of affairs has occurred with every language that
 supports macros.

 If you want a nauseous example, check out the Boost C preprocessor
 metaprogramming library. Or C++ expression templates - so cool, and yet
 so utterly wretched.

 Reminds me of a story from the 1980s. Microsoft's MASM stood for "Macro
 Assembler". Inevitably, Microsoft programmers invented a pile of macros
 that sort of turned asm programming into a pseudo-high-level language.
 This was shipped with every copy of MASM.

 A friend of mine who worked at MS was once given the task of fixing a
 bug in 50K of MASM code written in this macro language. The author of it
 had long since moved on. Every coder assigned to this task failed. My
 friend got it fixed in a couple hours. He was asked by his astonished
 manager how he'd managed to do it:

 "I assembled the code into an object file. Then I disassembled it with
 Zortech's OBJ2ASM, figured out how to fix it, then submitted the
 disassembled code as the new source code."
I have a suspension that it's the name "macro" that is the problem here. It leaves a bad taste due to issues with macros in the past. -- /Jacob Carlborg
Apr 19
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Apr 18, 2017 at 05:30:31PM -0700, Walter Bright via Digitalmars-d wrote:
[...]
 Reminds me of a story from the 1980s. Microsoft's MASM stood for
 "Macro Assembler". Inevitably, Microsoft programmers invented a pile
 of macros that sort of turned asm programming into a pseudo-high-level
 language. This was shipped with every copy of MASM.
 
 A friend of mine who worked at MS was once given the task of fixing a
 bug in 50K of MASM code written in this macro language. The author of
 it had long since moved on. Every coder assigned to this task failed.
 My friend got it fixed in a couple hours. He was asked by his
 astonished manager how he'd managed to do it:
 
 "I assembled the code into an object file. Then I disassembled it with
 Zortech's OBJ2ASM, figured out how to fix it, then submitted the
 disassembled code as the new source code."
+1, Classic! T -- VI = Visual Irritation
Apr 19
prev sibling next sibling parent reply Kapps <opantm2+spam gmail.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 Hi all

   I've been wanting to have support for interpolated strings in 
 D for some time now that will allow you to write e.g.:

 auto a = 7;
 writeln( $"{a} times 3 is {a*3}" );

 Code speaks louder that words so I've made a PR that adds this 
 support to ddmd as a RFC [1].

 The compiler will basically lower the $"..." string to a mixin 
 that concatenates
 the expression parts of the (inside the {}) and the plain text 
 parts.

 I do recognize that this is not part of the 2017 H1 plan but I 
 also believe such smaller improvements added regularly can make 
 the language both more productive and attractive.

 In case this addition gets a thumbs up I will of course improve 
 test coverage and any needed quality of implementation.

 [1] https://github.com/dlang/dmd/pull/6703
C# got this feature recently. I didn't expect it to be a significant difference, but I do find it a substantial improvement. Not only does it clearly show what goes where, but it's so much cleaner and more convenient.
Apr 17
parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 17 April 2017 at 19:38:33 UTC, Kapps wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
 [...]
C# got this feature recently. I didn't expect it to be a significant difference, but I do find it a substantial improvement. Not only does it clearly show what goes where, but it's so much cleaner and more convenient.
I don't understand how writeln($"{a} times 3 is {a * 3}"); is even marginally better than writeln(a, " times 3 is ", a * 3); // look ma, works right now! It's not even fewer characters. Atila
Apr 20
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
 writeln($"{a} times 3 is {a * 3}");

 is even marginally better than

 writeln(a, " times 3 is ", a * 3);  // look ma, works right now!
Matching up the correct commas and quotes is a bit of a pain in the latter. Though I don't think it is a big deal... I think most string building is a mistake anyway, as in you should find some entirely different way to it rather than embedding variables like that. But if I am going to do it it, the writeln(a, ",", "\") is like my least favorite way (yet I use it a lot because it is in there)
Apr 20
prev sibling next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/20/2017 02:28 PM, Atila Neves wrote:
 I don't understand how

 writeln($"{a} times 3 is {a * 3}");

 is even marginally better than

 writeln(a, " times 3 is ", a * 3);  // look ma, works right now!

 It's not even fewer characters.

 Atila
The latter IS pretty good, I use it often. It's *VASTLY* better than format strings[1]. But, cognitively, I find the former much easier to read and write (Apparently many other people seem to as well, although perhaps not everyone). I'm not sure I can explain why especially well, but the visual parsing is just much simpler and more symmetric, and it's much easier to tell at a glance where the full, umm "string", starts and ends, and what's going inside the string and what isn't. [1] Format strings, even with the new compile-time improvements in the latest DMD, for one thing, they still fail at DRY: Ie, with format strings, even compile-time ones, you're listing your parameters TWICE - once to describe WHERE they go and how they're formatted, and then again SEPARATELY to select WHAT data to be rendered. And then format strings also have the downside that the "WHAT data" is specified out-of-band, making it making it easy to get things flipped around, and just generally harder to see at-a-glance what's going on (similar to how UFCS chains can be easier to read than a tree of nested function calls, because for UFCS chains the ordering is perfectly sequential, unlike nested function calls and format strings where the logic jumps around). TBH, I'm not all that excited about the compile-time enhancements to format strings simply because...they're still format strings. And not only that, but they're printf-style syntax which is a total unintuitive mess (C#'s format string syntax was vastly better...for a format string, anyway...) IMO, the only time a format string should be used instead of std.conv.text() or interpolated strings is when: 1. You're just rendering *one* value at a time with non-standard formatting options (ie, left-/right-justified, leading/trailing zeroes, etc). (Speaking of which, `interp` could really use some formatting features so this could be avoided, and for performance reasons.) 2. You need to support custom formatting specified at runtime (ex: software that supports displaying date/time in custom user-defined formats) but want to be lazy about it and not bother finding/writing a more user-friendly formatting syntax than printf-style (ie, extremely rare).
Apr 20
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Apr 20, 2017 at 03:32:18PM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
[...]
 IMO, the only time a format string should be used instead of
 std.conv.text() or interpolated strings is when:
 
 1. You're just rendering *one* value at a time with non-standard
 formatting options (ie, left-/right-justified, leading/trailing
 zeroes, etc). (Speaking of which, `interp` could really use some
 formatting features so this could be avoided, and for performance
 reasons.)
 
 2. You need to support custom formatting specified at runtime (ex:
 software that supports displaying date/time in custom user-defined
 formats) but want to be lazy about it and not bother finding/writing a
 more user-friendly formatting syntax than printf-style (ie, extremely
 rare).
Hmm. I wonder if this is a matter of habituation and the kind of use cases you most commonly encounter. Having programmed in Perl extensively as well as in C/C++/D, I've dealt with both kinds of syntaxes, and I find that each has its own niche where it does best, while for use cases outside that niche it still works but not as well as the other syntax. For example, if you are printing lots and lots of text with only the occasional variable, the interpolated syntax is far more readable, e.g.: #!/usr/bin/perl print <<END Dear $title $name, This is a spam email sent by $companyName corporation on behalf of $evilAdvertisingCompany to solicit for a donation of $dollarAmount to the lobbying against anti-spam bills proposed by the government of $country. Yours truly, $spammerName END; is far more readable (and maintainable!) than: writefln(q"END Dear %s %s, This is a spam email sent by %s corporation on behalf of %s to solicit for a donation of $%d to the lobbying against anti-spam bills proposed by the government of %s. Yours truly, %s END", title, name, companyName, evilAdvertisingCompany, dollarAmount, country, spammerName); Much of this is due to the out-of-band issue you mentioned. Somebody could easily write the arguments in the wrong order, or substitute a variable with another one not meant to be formatted, and it would be difficult to notice the mistake just by looking at the code. But if you're printing lots of variables according to a precise template (e.g., rows of a table or a list of fields), format strings make more sense, e.g.: foreach (rec; records) { writefln("[%8d] %20s %10.3f", rec.id, rec.name, rec.amount); writefln(" %20s %10s", rec.altName, rec.comment); writefln(" %20s %6s", rec.address, rec.postalCode); } The advantage here is that you separate formatting from content, ostensibly a good thing depending on which circles you hang out in. And you can't beat this one with interpolated strings: auto matrix = [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]; writefln("Matrix:\n%([ %(%3d, %) ]%|\n%)", matrix); Output: Matrix: [ 1, 2, 3 ] [ 4, 5, 6 ] [ 7, 8, 9 ] If you're doing internationalization, though, neither option is a good one (I gave an example using dates in another post): printf-style formats have ordering issues (is it year first, then month, then day? Or month first then day then year? Which argument is which?), and interpolated strings have the problem of exposing variable names to the translators (who are probably non-coders), potentially opening up the possibility of arbitrary code execution via l10n strings. In this case, it would seem best to have named arguments with format strings. Between these textbook cases, though, is plenty of gray areas where it's debatable whether one syntax is clearly superior over the other(s). And here, factors of what you're used to, the kind of output you usually need to produce, etc., all come into play and there doesn't seem to be a clear one-size-fits-all. T -- Why ask rhetorical questions? -- JC
Apr 20
next sibling parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/20/2017 04:43 PM, H. S. Teoh via Digitalmars-d wrote:
 But if you're printing lots of variables according to a precise template
 (e.g., rows of a table or a list of fields), format strings make more
 sense, e.g.:

 	foreach (rec; records) {
 		writefln("[%8d] %20s  %10.3f", rec.id, rec.name, rec.amount);
 		writefln("      %20s  %10s", rec.altName, rec.comment);
 		writefln("      %20s  %6s", rec.address, rec.postalCode);
 	}
Meh, I'd argue that's just an example of why interpolated strings need support for formatting: foreach (rec; records) { writeln(%"[%8{rec.id}] %20{rec.name } %10.3{rec.amount}"); writeln(%" %20{rec.altName} %10{rec.comment}"); writeln(%" %20{rec.address} %6{rec.postalCode}"); }
 And you can't beat this one with interpolated strings:

 	auto matrix = [
 		[ 1, 2, 3 ],
 		[ 4, 5, 6 ],
 		[ 7, 8, 9 ]
 	];
 	writefln("Matrix:\n%([ %(%3d, %) ]%|\n%)", matrix);
That's actually a perfect example of why I don't like printf-style formatting syntax. I've seen regexes that were more readable.
 If you're doing internationalization, though, neither option is a good
 one (I gave an example using dates in another post): printf-style
 formats have ordering issues (is it year first, then month, then day? Or
 month first then day then year? Which argument is which?), and
There's an extension to printf (which, IIRC, D's format supports) that allows reordering of arguments, much like C#'s format strings, by specifying the argument numerically. But it's an u-g-l-y syntax. Hard to remember, too. And why bother referring to the args numerically when you can have a format string that simply refers to them by name instead (ie, interpolated strings)?
 interpolated strings have the problem of exposing variable names to the
 translators (who are probably non-coders), potentially opening up the
 possibility of arbitrary code execution via l10n strings. In this case,
 it would seem best to have named arguments with format strings.
You're basically describing a templating engine: Which are essentially just interpolated strings with sandboxing (or alternatively, interpolated strings are just embedded templating engines). And yes, I'd rather use a templating engine for non-coder translators than anything I've seen built-into any programming language: whether format string or interpolated string. But the last thing I'd hand them is anything with printf syntax.
 Between these textbook cases, though, is plenty of gray areas where it's
 debatable whether one syntax is clearly superior over the other(s). And
 here, factors of what you're used to, the kind of output you usually
 need to produce, etc., all come into play and there doesn't seem to be a
 clear one-size-fits-all.
When I see lots of options that are all basically the same thing, but each have their own little limitations and drawbacks (thus limiting each one's applicability to mere subsections of the overall problem domain)...well...that really bugs the shit out of me. ;) It's a big, loud, flashing sign that everybody's designs have gotten things wrong and none of them have quite hit the right mark. It makes me want to consolidate: Break down the limitations that divide the current options, reach the true "core" of the problem, and show that they're not really all separate tools, but mere approximations of the proper generalization that simply needed to be found and brought out. It's analogous to mathematical models: When the equations are complex and special-cased (like the rift between Newtonian physics and quantum physics) - that's how they know they probably got the model wrong. When they wind up with something simple, elegant and just-works, that's a sign they may be on the right track. Luckily, good general-purpose tools are a lot easier to come up with than unified field theory. ;) If scientists acted like programmers, the solar system model would still be stuck at the pre-Copernicus "All those planets have VERY COMPLEX movements! Very, VERY complex!! Good thing we have this big ol' toolbox with all the many, many tools involved in modeling all those complexities and special cases!" Basically, I'm the antithesis of a polyglot programmer :)
Apr 20
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 20 April 2017 at 20:43:35 UTC, H. S. Teoh wrote:
 If you're doing internationalization, though, neither option is 
 a good one (I gave an example using dates in another post): 
 printf-style formats have ordering issues (is it year first, 
 then month, then day? Or month first then day then year? Which 
 argument is which?), and interpolated strings have the problem 
 of exposing variable names to the translators (who are probably 
 non-coders), potentially opening up the possibility of 
 arbitrary code execution via l10n strings.
This is machine checkable though, the translation code could at least check that the interpolated strings in the translations match the source string's interpolations (possibly in a different order). An issue remaining is: if successive interpolated values depend on the order they are evaluated in, translations can still cause bugs (when they change the order).
Apr 21
prev sibling next sibling parent reply Gary Willoughby <dev nomad.so> writes:
On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
 I don't understand how

 writeln($"{a} times 3 is {a * 3}");

 is even marginally better than

 writeln(a, " times 3 is ", a * 3);  // look ma, works right now!

 It's not even fewer characters.

 Atila
This! This is bloat that doesn't need adding. D is complicated already, stop adding more 'stuff' and fix what's already there. I wish more time was taken up on libraries and tooling than completely unnecessary features like this.
Apr 21
parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/21/2017 04:11 AM, Gary Willoughby wrote:
 On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
 I don't understand how

 writeln($"{a} times 3 is {a * 3}");

 is even marginally better than

 writeln(a, " times 3 is ", a * 3);  // look ma, works right now!

 It's not even fewer characters.

 Atila
This! This is bloat that doesn't need adding. D is complicated already, stop adding more 'stuff' and fix what's already there. I wish more time was taken up on libraries and tooling than completely unnecessary features like this.
"Completely unnecessary" features like that are exactly what make D worthwhile in the first place. Otherwise may as well stick to C++ or Java.
Apr 21
parent reply Gary Willoughby <dev nomad.so> writes:
On Friday, 21 April 2017 at 12:32:01 UTC, Nick Sabalausky 
(Abscissa) wrote:
 "Completely unnecessary" features like that are exactly what 
 make D worthwhile in the first place. Otherwise may as well 
 stick to C++ or Java.
Multiple ways of doing the same thing are not valuable or progressive. Go and Rust are both smashing D in popularity and user share, maybe we could learn why that's the case.
Apr 21
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
Gary Willoughby wrote:

 Go and Rust are both smashing D in popularity and user share, maybe we 
 could learn why that's the case.
'cause go backed by google, and rust backed by mozilla.
Apr 21
prev sibling next sibling parent Anonymouse <asdf asdf.net> writes:
On Friday, 21 April 2017 at 12:45:39 UTC, Gary Willoughby wrote:
 Multiple ways of doing the same thing are not valuable or 
 progressive.

 Go and Rust are both smashing D in popularity and user share, 
 maybe we could learn why that's the case.
Corporate backing and word-of-mouth? I recall reading a KDE blog article about the problems of internationalizaton where you have large tables of pattern strings (think WelcomeMessage[EN]="Welcome $user to $hostname") where the tokens were very much in different ordering depending on language, or how some may even be considered implicit and ommitted outright. (Japanese springs to mind.) It was way back now so I don't even know where to begin to start looking, but it highlighted how the ordering did not translate well into printf patterns. Citation needed but it's a use-case this would address.
Apr 21
prev sibling next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Friday, 21 April 2017 at 12:45:39 UTC, Gary Willoughby wrote:
 Go and Rust are both smashing D in popularity and user share, 
 maybe we could learn why that's the case.
Can't rely on RAII, can't rely on GC. This is the single biggest problem to me. GC performs slowly, and RAII is inconsistent(no default ctors, can be nested in GC objects, etc..)
Apr 21
prev sibling parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/21/2017 08:45 AM, Gary Willoughby wrote:
 On Friday, 21 April 2017 at 12:32:01 UTC, Nick Sabalausky (Abscissa) wrote:
 "Completely unnecessary" features like that are exactly what make D
 worthwhile in the first place. Otherwise may as well stick to C++ or
 Java.
Multiple ways of doing the same thing are not valuable or progressive. Go and Rust are both smashing D in popularity and user share, maybe we could learn why that's the case.
As other have mentioned, there are plenty of far more more likely reasons for go/rust's popularity over D. As for not having multiple ways of doing things, that's what single-paradigm languages like Haskell or early versions of Java are for. D is deliberately multi-paradigm, which automatically throws the whole "python zen" philosophy right out the window: D is about letting the user choose the right tool for the job, not dictating that every shape of peg must be jammed into the square hole.
Apr 21
prev sibling parent Kapps <opantm2+spam gmail.com> writes:
On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
 On Monday, 17 April 2017 at 19:38:33 UTC, Kapps wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen 
 wrote:
 [...]
C# got this feature recently. I didn't expect it to be a significant difference, but I do find it a substantial improvement. Not only does it clearly show what goes where, but it's so much cleaner and more convenient.
I don't understand how writeln($"{a} times 3 is {a * 3}"); is even marginally better than writeln(a, " times 3 is ", a * 3); // look ma, works right now! It's not even fewer characters. Atila
I find the first one to be cleaner honestly. It shows exactly where values are coming from, doesn't have a bunch of commas that can be annoying to match up, and doesn't start and stop a string multiple times. And for the second one, that's only because writeln specifically includes overloads to take in multiple objects and merge them together. This isn't something functions should have to always do (except in the cases where taking in multiple arguments is actually more efficient, like presumably with writeln since it doesn't need to actually merge the string). It's not like it's a huge missing feature. Having something like the below isn't too bad: foo(format("Value of ", a, " * 3 is ", a * 3, ".")); But it's just not as nice as: foo($"Value of {a} * 3 is {a * 3}.");
Apr 21
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/15/2017 1:04 PM, Jonas Drewsen wrote:
[...]
Thanks for doing the work to make a sample implementation, too. I don't know if this will make it into D, but Jonas is a fine example of a champion.
Apr 18
parent reply Jonas Drewsen <nospam4321 hotmail.com> writes:
On Tuesday, 18 April 2017 at 08:42:38 UTC, Walter Bright wrote:
 On 4/15/2017 1:04 PM, Jonas Drewsen wrote:
[...]
Thanks for doing the work to make a sample implementation, too. I don't know if this will make it into D, but Jonas is a fine example of a champion.
Thanks for the feedback. Nice to know that it is not immediately off the table at least :)
Apr 19
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/19/2017 5:04 AM, Jonas Drewsen wrote:
 On Tuesday, 18 April 2017 at 08:42:38 UTC, Walter Bright wrote:
 On 4/15/2017 1:04 PM, Jonas Drewsen wrote:
 [...]
Thanks for doing the work to make a sample implementation, too. I don't know if this will make it into D, but Jonas is a fine example of a champion.
Thanks for the feedback. Nice to know that it is not immediately off the table at least :)
I can't promise anything. But it does need a lot of work. A survey of how it is done in other languages is helpful to see what they find useful and to see what has been overlooked. A quick look by me shows a lot of variety. D's lambda syntax came about after such a survey, and we cherry-picked the best characteristics of the lot. The result was a nice home run for D. The n.g. discussion also brought up a couple of existing library solutions. Those need to be evaluated as well and compared with any language solution.
Apr 19
parent Walter Bright <newshound2 digitalmars.com> writes:
I forgot to mention - the pros and cons of whether the string interpolation is 
compile time or run time is a critical decision.
Apr 19
prev sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
   I've been wanting to have support for interpolated strings in 
 D for some time now that will allow you to write e.g.:
 [...]
One place I'd appreciate interpolated strings is as an option when working with heredoc strings. These strings are often multiple lines or paragraphs, using format style construction is often error-prone due to length. --Jon
Apr 18
parent Faux Amis <faux amis.com> writes:
On 2017-04-19 03:45, Jon Degenhardt wrote:
 On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
   I've been wanting to have support for interpolated strings in D for 
 some time now that will allow you to write e.g.:
 [...]
One place I'd appreciate interpolated strings is as an option when working with heredoc strings. These strings are often multiple lines or paragraphs, using format style construction is often error-prone due to length. --Jon
So, that makes 2 otherwise error-prone use-cases. The first being keeping arguments & format string in sync (Jonathan Marler). And they actually compound! (Large strings get out of sync quicker I would guess) And, I would like to add that this is are very newby friendly feature. Nowadays people might even expect a language to have this.
Apr 20