www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - string interpolation fun

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Another idea I had for solving a different problem with string 
interpolation: better named-field struct initialization.

The below compiles and runs as expected with 
https://github.com/dlang/dmd/pull/7988

//
// build T with parameters using string interpolation
//
T make(T, Args...)() if (Args.length > 0 && Args.length % 2 == 0)
{
     T result;
     import std.string;
     static foreach(i; 0 .. Args.length / 2)
     {{
         // remove all punctuation and spaces
          static assert(is(typeof(Args[i * 2]) == string));
         enum symbol = Args[i * 2].strip(" :=,");
         static if(symbol.length > 0)
         {
             __traits(getMember, result, symbol) = Args[i * 2 + 1];
         }
         else
         {
             __traits(getMember, result, __traits(identifier, Args[i * 2 
+ 1])) = Args[i * 2 + 1];
         }
     }}
     return result;
}

struct S
{
     int x;
     int y;
}
void main()
{
     import std.stdio;
     int x = 1;
     int y = 2;
     auto s = make!(S, i"x: $(y), y: $(x)"); // name the fields to assign
     auto s2 = make!(S, i":$(x), :$(y)"); // use symbol name as field name
     auto s3 = make!(S, i"x: $(6), y: $(7)"); // use literals to assign
     writeln(i"$(s), $(s2), $(s3)"); // runtime parameters
}


One thing we are missing here, is a sure-fire way to know which parts of 
the CT-sequence are literal, and which ones are parameters. For 
instance, at first I tried this for s2:

make!(S, i"$(x), $(y)");

but it failed because there is no prefix string for x. I couldn't think 
of a good static if condition that checked to see if the parameter was 
string literal or not, without also capturing string expressions inside 
the $().

Any ideas?

-Steve
Dec 13 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/13/18 10:53 AM, Steven Schveighoffer wrote:
 void main()
 {
      import std.stdio;
      int x = 1;
      int y = 2;
      auto s = make!(S, i"x: $(y), y: $(x)"); // name the fields to assign
      auto s2 = make!(S, i":$(x), :$(y)"); // use symbol name as field name
      auto s3 = make!(S, i"x: $(6), y: $(7)"); // use literals to assign
      writeln(i"$(s), $(s2), $(s3)"); // runtime parameters
 }
 
 
Also, this would be a lot less noisy if we didn't need the parens after the $, but the PR doesn't support that (yet?). -Steve
Dec 13 2018
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 13 December 2018 at 16:01:52 UTC, Steven 
Schveighoffer wrote:
 On 12/13/18 10:53 AM, Steven Schveighoffer wrote:
 void main()
 {
      import std.stdio;
      int x = 1;
      int y = 2;
      auto s = make!(S, i"x: $(y), y: $(x)"); // name the 
 fields to assign
      auto s2 = make!(S, i":$(x), :$(y)"); // use symbol name 
 as field name
      auto s3 = make!(S, i"x: $(6), y: $(7)"); // use literals 
 to assign
      writeln(i"$(s), $(s2), $(s3)"); // runtime parameters
 }
 
 
Also, this would be a lot less noisy if we didn't need the parens after the $, but the PR doesn't support that (yet?). -Steve
Very cool use case. Adding support for the `$foo` case (no parens) would be fairly trival, but requires making a decision of what type of grammar node/token to use. We could use the same mechanism that templates use, but that would mean that something like $foo.max would be $(foo).max instead of $(foo.max) which seems a bit unexpected. That would be a good thing include in the DIP, a list of the obvious tokens/grammar nodes we could use and their pros/cons.
Dec 13 2018
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/13/18 11:21 AM, Jonathan Marler wrote:

 Adding support for the `$foo` case (no parens) would be fairly trival, 
 but requires making a decision of what type of grammar node/token to 
 use.  We could use the same mechanism that templates use, but that would 
 mean that something like $foo.max would be $(foo).max instead of 
 $(foo.max) which seems a bit unexpected.  That would be a good thing 
 include in the DIP, a list of the obvious tokens/grammar nodes we could 
 use and their pros/cons.
Yeah, I would recommend $foo.max be $(foo.max). This isn't like templates, where the dot is meaningful both ways. -Steve
Dec 13 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/13/18 11:29 AM, Steven Schveighoffer wrote:
 On 12/13/18 11:21 AM, Jonathan Marler wrote:
 
 Adding support for the `$foo` case (no parens) would be fairly trival, 
 but requires making a decision of what type of grammar node/token to 
 use.  We could use the same mechanism that templates use, but that 
 would mean that something like $foo.max would be $(foo).max instead of 
 $(foo.max) which seems a bit unexpected.  That would be a good thing 
 include in the DIP, a list of the obvious tokens/grammar nodes we 
 could use and their pros/cons.
Yeah, I would recommend $foo.max be $(foo.max). This isn't like templates, where the dot is meaningful both ways.
I'd say it's more like the address operator: &foo.member means &(foo.member), not &(foo).member -Steve
Dec 13 2018
prev sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 13 December 2018 at 16:21:48 UTC, Jonathan Marler 
wrote:
 On Thursday, 13 December 2018 at 16:01:52 UTC, Steven 
 Schveighoffer wrote:
 On 12/13/18 10:53 AM, Steven Schveighoffer wrote:
 void main()
 {
      import std.stdio;
      int x = 1;
      int y = 2;
      auto s = make!(S, i"x: $(y), y: $(x)"); // name the 
 fields to assign
      auto s2 = make!(S, i":$(x), :$(y)"); // use symbol name 
 as field name
      auto s3 = make!(S, i"x: $(6), y: $(7)"); // use literals 
 to assign
      writeln(i"$(s), $(s2), $(s3)"); // runtime parameters
 }
 
 
Also, this would be a lot less noisy if we didn't need the parens after the $, but the PR doesn't support that (yet?). -Steve
Very cool use case. Adding support for the `$foo` case (no parens) would be fairly trival, but requires making a decision of what type of grammar node/token to use. We could use the same mechanism that templates use, but that would mean that something like $foo.max would be $(foo).max instead of $(foo.max) which seems a bit unexpected. That would be a good thing include in the DIP, a list of the obvious tokens/grammar nodes we could use and their pros/cons.
I should add, the strategy I used in the PR was to establish the "foundation" of interpolated strings and postpone any unnecessary decisions so they didn't prevent the foundation from being merged. The idea was to be able to merge the change so that it could be experimented with without disturbing the rest of the compiler. After it was merged and we could get some experience with it and we could later decide what features to add later. I was pleasantly surprised at how little the new feature affected the compiler. I didn't even need a grammar change. I just modified the lexer to detect the letter 'i' before string literals and then in the parse stage lowered those string literals to tuples. It only affected the existing compiler in 2 places, very low impact and encapsulated.
Dec 13 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/13/18 11:36 AM, Jonathan Marler wrote:
 On Thursday, 13 December 2018 at 16:21:48 UTC, Jonathan Marler wrote:
 On Thursday, 13 December 2018 at 16:01:52 UTC, Steven Schveighoffer 
 wrote:
 On 12/13/18 10:53 AM, Steven Schveighoffer wrote:
 void main()
 {
      import std.stdio;
      int x = 1;
      int y = 2;
      auto s = make!(S, i"x: $(y), y: $(x)"); // name the fields to 
 assign
      auto s2 = make!(S, i":$(x), :$(y)"); // use symbol name as 
 field name
      auto s3 = make!(S, i"x: $(6), y: $(7)"); // use literals to assign
      writeln(i"$(s), $(s2), $(s3)"); // runtime parameters
 }
Also, this would be a lot less noisy if we didn't need the parens after the $, but the PR doesn't support that (yet?).
Very cool use case. Adding support for the `$foo` case (no parens) would be fairly trival, but requires making a decision of what type of grammar node/token to use.  We could use the same mechanism that templates use, but that would mean that something like $foo.max would be $(foo).max instead of $(foo.max) which seems a bit unexpected.  That would be a good thing include in the DIP, a list of the obvious tokens/grammar nodes we could use and their pros/cons.
I should add, the strategy I used in the PR was to establish the "foundation" of interpolated strings and postpone any unnecessary decisions so they didn't prevent the foundation from being merged.  The idea was to be able to merge the change so that it could be experimented with without disturbing the rest of the compiler.  After it was merged and we could get some experience with it and we could later decide what features to add later.
So I have a beef with the assertion from Andrei that since nobody is using the current interpolation possibilities using mixins, it translates to people not wanting to do string interpolation. And my reasoning is: if it's super-ugly and complicated, people *aren't* going to use it. The beauty of it is one of the most important parts of adding it to the compiler! And the beauty goes along with getting rid of those parentheses. With current capabilities you can get rid of the parentheses, but if interpolation goes into the compiler, it's functionality is fixed. I think we should make a decision either way and propose it in the DIP.
 
 I was pleasantly surprised at how little the new feature affected the 
 compiler. I didn't even need a grammar change. I just modified the lexer 
 to detect the letter 'i' before string literals and then in the parse 
 stage lowered those string literals to tuples.  It only affected the 
 existing compiler in 2 places, very low impact and encapsulated.
 
Yeah, it's pretty sweet how it works. I very much hope we can get it in. As people think of string interpolation possibilities, they should post threads on it. It would be good to do a study on how it looks/performs with current capabilities and with the proposed update. -Steve
Dec 13 2018
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 13 December 2018 at 16:58:35 UTC, Steven 
Schveighoffer wrote:
 So I have a beef with the assertion from Andrei that since 
 nobody is using the current interpolation possibilities using 
 mixins, it translates to people not wanting to do string 
 interpolation. And my reasoning is: if it's super-ugly and 
 complicated, people *aren't* going to use it. The beauty of it 
 is one of the most important parts of adding it to the compiler!
I think another big part of it is that currently, using string interpolation requires pulling in a dub package as a dependency. Especially in small projects, the difference between "zero dependencies" and ">=1 dependencies" is pretty huge. If `interp` were in the standard library (std.string, maybe?) it would probably see more use than it does now (at least, I'd use it in my projects).
Dec 13 2018
parent David Gileadi <gileadisNOSPM gmail.com> writes:
On 12/13/18 1:19 PM, Paul Backus wrote:
 On Thursday, 13 December 2018 at 16:58:35 UTC, Steven Schveighoffer wrote:
 So I have a beef with the assertion from Andrei that since nobody is 
 using the current interpolation possibilities using mixins, it 
 translates to people not wanting to do string interpolation. And my 
 reasoning is: if it's super-ugly and complicated, people *aren't* 
 going to use it. The beauty of it is one of the most important parts 
 of adding it to the compiler!
I think another big part of it is that currently, using string interpolation requires pulling in a dub package as a dependency. Especially in small projects, the difference between "zero dependencies" and ">=1 dependencies" is pretty huge. If `interp` were in the standard library (std.string, maybe?) it would probably see more use than it does now (at least, I'd use it in my projects).
Having recently been doing some typescript programming, I can say that string interpolation is the kind of thing that grows on you. I could live without it, but code with it is much nicer. And more likely correct, as has been demonstrated in this thread. I can say, however, that the barrier to entry of a library version, both in imports and in heavy syntax, are enough that I'd never use one.
Dec 13 2018