www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - proposal: a new string litteral to embed variables in a string

reply Timothee Cour <thelastmammoth gmail.com> writes:
the proposed new syntax
r{var1= a; and var2= foo}

is replaced by a tuple:
("var1=", a, "; and var2=", foo)
where   denotes escaping symbols.

  itself be escaped with \ .

optionally, expressions can be incorporated:
r{a2= (a*2); and fooU= (foo.toUpper)}

I've actually already implemented this feature via a mixin, and find it
extremely useful, but removing the mixing via this proposal would make it
even more palatable. It works using a simple grammar that searches for
valid identifiers after a  , or finds nested expressions nested inside
parenthesis (arbitrary nesting allowed).

Proper tooling will syntax highlight correctly the nested variables.


This feature is especially useful when there are a few variables involved,
as alternatives are clunky


use cases:
A) simple string formatting (eg: formattedWrite)
----
q{first var= a, second= b, third= c!}.foo
vs:
("first var=",a," second=",b," third=",c,"!").foo // `",,"` for a single ` `
("first var=%s, second=%s, third=%s!", a, b, c).foo => // error prone esp
with many variables
or alternatives involving ~ :
("first var="~a.to!string~", second="~b.to!string~",
third="~c.to!string~"!").foo
//clunky

B) parsing (eg formattedRead)
same advantages as above
Oct 31 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-10-31 22:24, Timothee Cour wrote:
 the proposed new syntax
 r{var1= a; and var2= foo}

 is replaced by a tuple:
 ("var1=", a, "; and var2=", foo)
 where   denotes escaping symbols.

   itself be escaped with \ .

 optionally, expressions can be incorporated:
 r{a2= (a*2); and fooU= (foo.toUpper)}
Would be the best symbol for this. It might get confusing with UDA's. -- /Jacob Carlborg
Oct 31 2013
next sibling parent reply Timothee Cour <thelastmammoth gmail.com> writes:
$ is another obvious choice (eg in shell expansion)



On Thu, Oct 31, 2013 at 2:37 PM, Jacob Carlborg <doob me.com> wrote:

 On 2013-10-31 22:24, Timothee Cour wrote:

 the proposed new syntax
 r{var1= a; and var2= foo}

 is replaced by a tuple:
 ("var1=", a, "; and var2=", foo)
 where   denotes escaping symbols.

   itself be escaped with \ .

 optionally, expressions can be incorporated:
 r{a2= (a*2); and fooU= (foo.toUpper)}
Would be the best symbol for this. It might get confusing with UDA's. -- /Jacob Carlborg
Oct 31 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-10-31 22:47, Timothee Cour wrote:
 $ is another obvious choice (eg in shell expansion)

-- /Jacob Carlborg
Nov 01 2013
next sibling parent reply Timothee Cour <thelastmammoth gmail.com> writes:
actually an important use case of this feature is to help writing domain
specific language inputs, eg writing a python file inside D, or config /
plain text files.


  would be inside string literal so should cause little confusion from D's
side, but for example would force one to escape ' ' in email addresses
(more rare).

Frankly, this is bike shedding though; let's assume we pick one in
http://www.ascii-code.com/ and focus on whether we can agree on this
feature.

I'm using it extensively for great benefit: more DRY code, less spurious
files, cleaner integration with D.



On Fri, Nov 1, 2013 at 12:33 AM, Jacob Carlborg <doob me.com> wrote:

 On 2013-10-31 22:47, Timothee Cour wrote:

 $ is another obvious choice (eg in shell expansion)

-- /Jacob Carlborg
Nov 05 2013
next sibling parent reply "Daniel Davidson" <nospam spam.com> writes:
On Tuesday, 5 November 2013 at 22:26:23 UTC, Timothee Cour wrote:
 Frankly, this is bike shedding though; let's assume we pick one 
 in
 http://www.ascii-code.com/ and focus on whether we can agree on 
 this
 feature.

 I'm using it extensively for great benefit: more DRY code, less 
 spurious
 files, cleaner integration with D.
Isn't this just string interpolation which many/most languages have? Yes - it is a great feature. Do you have working code on github that others may use? Thanks Dan
Nov 05 2013
next sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
On Tue, Nov 5, 2013 at 3:19 PM, Daniel Davidson <nospam spam.com> wrote:
 Isn't this just string interpolation which many/most languages have?
 Yes - it is a great feature. Do you have working code on github that
 others may use?

 Thanks
 Dan
This is more powerful than string interpolation, as it generates a tuple instead of generating a string: in my proposal, r{var1= a; and var2= foo} is replaced by ("var1=", a, "; and var2=", foo) instead of text("var1=,a,"; and var2=",foo), as done in most languages like you mention. This exploits D's powerful variadic templates and is more powerful: we can get string interpolation by simply using r{...}.text but we can do more fancy things if needed (eg apply formatting, etc), without requiring additional types of string litterals, as needed in scala ( http://docs.scala-lang.org/overviews/core/string-interpolation.html)
Nov 05 2013
prev sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
On Tue, Nov 5, 2013 at 3:19 PM, Daniel Davidson <nospam spam.com> wrote:

 Do you have working code on github that others may use?
Let me try to post it soon.
Nov 05 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-11-05 23:26, Timothee Cour wrote:
 actually an important use case of this feature is to help writing domain
 specific language inputs, eg writing a python file inside D, or config /
 plain text files.


   would be inside string literal so should cause little confusion from
 D's side, but for example would force one to escape ' ' in email
 addresses (more rare).
If we're talking about writing other languages inside D, then " " would be in conflict with Objective-C which uses that for all new syntax (that isn't available in plain C). -- /Jacob Carlborg
Nov 06 2013
parent reply Timothee Cour <thelastmammoth gmail.com> writes:
On Wed, Nov 6, 2013 at 2:43 AM, Jacob Carlborg <doob me.com> wrote:

 On 2013-11-05 23:26, Timothee Cour wrote:

 actually an important use case of this feature is to help writing domain
 specific language inputs, eg writing a python file inside D, or config /
 plain text files.


   would be inside string literal so should cause little confusion from
 D's side, but for example would force one to escape ' ' in email
 addresses (more rare).
If we're talking about writing other languages inside D, then " " would be in conflict with Objective-C which uses that for all new syntax (that isn't available in plain C).
How about parametrizing the escape string: r{var= a} // by default r{ }{var= a} //same as above r{$}{var=$a} //same as above
Nov 06 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-11-06 21:33, Timothee Cour wrote:

 How about parametrizing the escape string:
 r{var= a} //  by default
 r{ }{var= a} //same as above
 r{$}{var=$a} //same as above
Looks like this is getting quite complicated. -- /Jacob Carlborg
Nov 07 2013
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
While thinking on a new string literal that may support DSL's, the syntax
should optionally receive a language specification on opening.
The thing that sucks most about DSL's is that the IDE can't syntax hilight
them, but if it was provided what language the string was, then a smart IDE
could apply syntax hilighting for that language within the scring scope.
Eg, this sort of thing is supported in html, which is usually hilighted
correctly by IDE's:
  <script type="text/javascript"> or <script language="javascript">

And exists in many other places too.


On 6 November 2013 08:26, Timothee Cour <thelastmammoth gmail.com> wrote:

 actually an important use case of this feature is to help writing domain
 specific language inputs, eg writing a python file inside D, or config /
 plain text files.


   would be inside string literal so should cause little confusion from D's
 side, but for example would force one to escape ' ' in email addresses
 (more rare).

 Frankly, this is bike shedding though; let's assume we pick one in
 http://www.ascii-code.com/ and focus on whether we can agree on this
 feature.

 I'm using it extensively for great benefit: more DRY code, less spurious
 files, cleaner integration with D.



 On Fri, Nov 1, 2013 at 12:33 AM, Jacob Carlborg <doob me.com> wrote:

 On 2013-10-31 22:47, Timothee Cour wrote:

 $ is another obvious choice (eg in shell expansion)

-- /Jacob Carlborg
Nov 05 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-11-06 02:42, Manu wrote:
 While thinking on a new string literal that may support DSL's, the
 syntax should optionally receive a language specification on opening.
 The thing that sucks most about DSL's is that the IDE can't syntax
 hilight them, but if it was provided what language the string was, then
 a smart IDE could apply syntax hilighting for that language within the
 scring scope.
 Eg, this sort of thing is supported in html, which is usually hilighted
 correctly by IDE's:
    <script type="text/javascript"> or <script language="javascript">

 And exists in many other places too.
Github flavored Markdown uses: ```javascript // JavaScript code ``` -- /Jacob Carlborg
Nov 06 2013
prev sibling parent reply Timothee Cour <thelastmammoth gmail.com> writes:
On Tue, Nov 5, 2013 at 5:42 PM, Manu <turkeyman gmail.com> wrote:

 While thinking on a new string literal that may support DSL's, the syntax
 should optionally receive a language specification on opening.
 The thing that sucks most about DSL's is that the IDE can't syntax hilight
 them, but if it was provided what language the string was, then a smart IDE
 could apply syntax hilighting for that language within the scring scope.
 Eg, this sort of thing is supported in html, which is usually hilighted
 correctly by IDE's:
   <script type="text/javascript"> or <script language="javascript">

 And exists in many other places too.
I agree. For that suggest the following syntax (independent of this proposal): That is, support UDA for expressions. ---- void main(){ import std.conv:text; int var=12; ("syntax=python") r{ def fun: print( var) #will expand to tuple element 12 print( (var.to!string)) #will expand to tuple element "12" } .text .run_python; void run_python(string a){ import std.system; system("python -c "~a.escapeShellFileName); } } ----
Nov 05 2013
parent reply "Chris Cain" <clcain uncg.edu> writes:
On Wednesday, 6 November 2013 at 02:38:57 UTC, Timothee Cour 
wrote:
 I agree. For that suggest the following syntax (independent of 
 this
 proposal):

 That is, support UDA for expressions.

 ----
 void main(){
   import std.conv:text;

   int var=12;

    ("syntax=python")
   r{
     ...snip...
   }
 }
 ----
I'd very nearly say that this could be a library function. Imagine it like this: --- syntax!"python"(r{ ... }) --- A bit more verbose than using a UDA but the return type could be something like SyntaxType!"python" and you could make it so that functions only take SyntaxType!"python" for some compile-time checking using the type system. For instance, if your function requires python code then you can specify it in the argument. I'd also like it to be implicitly convertable to a generic SyntaxType that accepts everything if you just don't care what kind of syntax it is. As long as it's part of the standard library, then IDEs could also take advantage of it. Not saying that they shouldn't also support UDAs, but just throwing that out there as another alternative.
Nov 05 2013
parent Timothee Cour <thelastmammoth gmail.com> writes:
On Tue, Nov 5, 2013 at 7:43 PM, Chris Cain <clcain uncg.edu> wrote:

 On Wednesday, 6 November 2013 at 02:38:57 UTC, Timothee Cour wrote:

 I agree. For that suggest the following syntax (independent of this
 proposal):

 That is, support UDA for expressions.

 ----
 void main(){
   import std.conv:text;

   int var=12;

    ("syntax=python")
   r{
     ...snip...
   }
 }
 ----
I'd very nearly say that this could be a library function. Imagine it like this: --- syntax!"python"(r{ ... }) --- A bit more verbose than using a UDA but the return type could be something like SyntaxType!"python" and you could make it so that functions only take SyntaxType!"python" for some compile-time checking using the type system. For instance, if your function requires python code then you can specify it in the argument. I'd also like it to be implicitly convertable to a generic SyntaxType that accepts everything if you just don't care what kind of syntax it is. As long as it's part of the standard library, then IDEs could also take advantage of it. Not saying that they shouldn't also support UDAs, but just throwing that out there as another alternative.
one problem with this: r{...} is converted to a built-in tuple, and we can't return built-in tuples from functions (only std.typecons.tuple, which has issues, eg unnecessary copying, can't return elements by ref, etc). Note that returning elements by ref (when needed) is crucial for use case B in the OT (formattedRead). On the otherhand, I also thought about using a modification of std.typetuple.Alias that gets an extra 1st argument to indicate syntax. It doesn't work because when there's an expression involved we get: Error: variable a cannot be read at compile time; eg: Alias!(a+b,"foo") and r{...} syntax is suppose to support arbitrary expressions and forward them as builtin tuple.
Nov 05 2013
prev sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
btw, another argument is type safety: my proposal has type safety inherited
from typesafe variadic templates, whereas the "%s" way of doing things is
fragile:

import std.stdio;
void main(){
  writefln("%s%s%s\n",1,2);//crash at RT
  writefln("%s%s\n",1,2,3);//silently accepts even though 1 more argument
  writefln("%s%d\n",1,"foo");//crash at RT: wrong format specifier
}


On Thu, Oct 31, 2013 at 2:37 PM, Jacob Carlborg <doob me.com> wrote:

 On 2013-10-31 22:24, Timothee Cour wrote:

 the proposed new syntax
 r{var1= a; and var2= foo}

 is replaced by a tuple:
 ("var1=", a, "; and var2=", foo)
 where   denotes escaping symbols.

   itself be escaped with \ .

 optionally, expressions can be incorporated:
 r{a2= (a*2); and fooU= (foo.toUpper)}
Would be the best symbol for this. It might get confusing with UDA's. -- /Jacob Carlborg
Oct 31 2013
prev sibling next sibling parent "Daniel Davidson" <nospam spam.com> writes:
On Thursday, 31 October 2013 at 21:24:42 UTC, Timothee Cour wrote:

 I've actually already implemented this feature via a mixin, and 
 find it
 extremely useful, but removing the mixing via this proposal 
 would make it
 even more palatable. It works using a simple grammar that 
 searches for
 valid identifiers after a  , or finds nested expressions nested 
 inside
 parenthesis (arbitrary nesting allowed).

 Proper tooling will syntax highlight correctly the nested 
 variables.
Would you mind posting your code? Thanks Dan
Nov 02 2013
prev sibling parent reply "Dicebot" <public dicebot.lv> writes:
Reasonable proposal but I have not encountered much need in this 
functionality personally (std.string.format worked just fine) and 
thus can't really evaluate how justified such addition may be.
Nov 06 2013
next sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Wednesday, 6 November 2013 at 03:44:07 UTC, Chris Cain wrote:
 I'd very nearly say that this could be a library function. 
 Imagine it like this:

 syntax!"python"(r{
 ...
 })
I agree. On Wednesday, 6 November 2013 at 11:45:41 UTC, Dicebot wrote:
 Reasonable proposal but I have not encountered much need in 
 this functionality personally (std.string.format worked just 
 fine) and thus can't really evaluate how justified such 
 addition may be.
I also agree.
Nov 06 2013
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
06-Nov-2013 15:45, Dicebot пишет:
 Reasonable proposal but I have not encountered much need in this
 functionality personally (std.string.format worked just fine) and thus
 can't really evaluate how justified such addition may be.
+1 Since formatting/templates became accessible in CTFE I do not see much need for this. -- Dmitry Olshansky
Nov 06 2013
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 7 November 2013 03:14, Dmitry Olshansky <dmitry.olsh gmail.com> wrote:

 06-Nov-2013 15:45, Dicebot =D0=BF=D0=B8=D1=88=D0=B5=D1=82:

  Reasonable proposal but I have not encountered much need in this
 functionality personally (std.string.format worked just fine) and thus
 can't really evaluate how justified such addition may be.
+1 Since formatting/templates became accessible in CTFE I do not see much need for this.
Every time I use a q{} block, it's to embed some code in a different language. I would like the IDE to syntax hilight properly if able. HLSL/GLSL, JSON, XML are the most common embedded languages I encounter.
Nov 06 2013
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
06-Nov-2013 21:40, Manu пишет:
 On 7 November 2013 03:14, Dmitry Olshansky <dmitry.olsh gmail.com
 <mailto:dmitry.olsh gmail.com>> wrote:

     06-Nov-2013 15:45, Dicebot пишет:

         Reasonable proposal but I have not encountered much need in this
         functionality personally (std.string.format worked just fine)
         and thus
         can't really evaluate how justified such addition may be.


     +1

     Since formatting/templates became accessible in CTFE I do not see
     much need for this.


 Every time I use a q{} block, it's to embed some code in a different
 language. I would like the IDE to syntax hilight properly if able.
 HLSL/GLSL, JSON, XML are the most common embedded languages I encounter.
Depending on editor/IDE a decent macro/extension would detect specific function before the q{}. Say: hlsl!q{ }; And highlight it as appropriate. I think it's customary for good editors to highlight parts of code based on different scheme (say PHP/JS mixed in the HTML highlights just fine). So the basic blocks must be there already. All in all it's hardly a good thing to trade a language feature *only* to help people give "hints" to IDEs (which they may as well ignore). -- Dmitry Olshansky
Nov 06 2013
parent reply Timothee Cour <thelastmammoth gmail.com> writes:
On Wed, Nov 6, 2013 at 10:49 AM, Dmitry Olshansky <dmitry.olsh gmail.com>wr=
ote:

 06-Nov-2013 21:40, Manu =D0=C9=DB=C5=D4:

 On 7 November 2013 03:14, Dmitry Olshansky <dmitry.olsh gmail.com
 <mailto:dmitry.olsh gmail.com>> wrote:

     06-Nov-2013 15:45, Dicebot =D0=C9=DB=C5=D4:

         Reasonable proposal but I have not encountered much need in this
         functionality personally (std.string.format worked just fine)
         and thus
         can't really evaluate how justified such addition may be.


     +1

     Since formatting/templates became accessible in CTFE I do not see
     much need for this.


 Every time I use a q{} block, it's to embed some code in a different
 language. I would like the IDE to syntax hilight properly if able.
 HLSL/GLSL, JSON, XML are the most common embedded languages I encounter.
Depending on editor/IDE a decent macro/extension would detect specific function before the q{}. Say: hlsl!q{ }; And highlight it as appropriate. I think it's customary for good editors to highlight parts of code based on different scheme (say PHP/JS mixed in the HTML highlights just fine). =
So
 the basic blocks must be there already.

 All in all it's hardly a good thing to trade a language feature *only* to
 help people give "hints" to IDEs (which they may as well ignore).
the proposal in the OT is not about syntax highlight hints, it's about embedding variables in string literals.
 --
 Dmitry Olshansky
Nov 06 2013
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
07-Nov-2013 00:24, Timothee Cour ÐÉÛÅÔ:
 On Wed, Nov 6, 2013 at 10:49 AM, Dmitry Olshansky <dmitry.olsh gmail.com
 <mailto:dmitry.olsh gmail.com>> wrote:

     06-Nov-2013 21:40, Manu ÐÉÛÅÔ:

         On 7 November 2013 03:14, Dmitry Olshansky
         <dmitry.olsh gmail.com <mailto:dmitry.olsh gmail.com>
         <mailto:dmitry.olsh gmail.com <mailto:dmitry.olsh gmail.com>>__>
         wrote:

              06-Nov-2013 15:45, Dicebot ÐÉÛÅÔ:

                  Reasonable proposal but I have not encountered much
         need in this
                  functionality personally (std.string.format worked just
         fine)
                  and thus
                  can't really evaluate how justified such addition may be.


              +1

              Since formatting/templates became accessible in CTFE I do
         not see
              much need for this.


         Every time I use a q{} block, it's to embed some code in a different
         language. I would like the IDE to syntax hilight properly if able.
         HLSL/GLSL, JSON, XML are the most common embedded languages I
         encounter.
[snip]
     All in all it's hardly a good thing to trade a language feature
     *only* to help people give "hints" to IDEs (which they may as well
     ignore).


 the proposal in the OT is not about syntax highlight hints, it's about
 embedding variables in string literals.
AFAICT there is no universal thing (literal syntax) that mix well into every DSL. On this fact alone I'd rather use a natural template engine for the DSL in question. That and fact that it's a whole new feature with its pitfalls is enough for me to at least postpone it. -- Dmitry Olshansky
Nov 06 2013
prev sibling parent reply Timothee Cour <thelastmammoth gmail.com> writes:
To those who don't see the use of this:

which code would you rather read & write? see pastebin:
http://dpaste.dzfl.pl/b9f65a39

Another advantage is that using an autoformatter won't mess up things
inside a r{..} literal.



On Wed, Nov 6, 2013 at 9:14 AM, Dmitry Olshansky <dmitry.olsh gmail.com>wro=
te:

 06-Nov-2013 15:45, Dicebot =D0=BF=D0=B8=D1=88=D0=B5=D1=82:

  Reasonable proposal but I have not encountered much need in this
 functionality personally (std.string.format worked just fine) and thus
 can't really evaluate how justified such addition may be.
+1 Since formatting/templates became accessible in CTFE I do not see much need for this. -- Dmitry Olshansky
Nov 06 2013
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
07-Nov-2013 01:02, Timothee Cour пишет:
 To those who don't see the use of this:

 which code would you rather read & write? see pastebin:
 http://dpaste.dzfl.pl/b9f65a39

 Another advantage is that using an autoformatter won't mess up things
 inside a r{..} literal.
Challenge accepted. 50 lines in current D2: import std.algorithm, std.conv, std.array, std.uni, std.stdio; auto embed(Vars...)(string tmpl) { auto app = appender!string(); alias Put = void delegate(); Put[string] fns; foreach(i, v; Vars) { //strangely v.stringof is "v" - BUG/FEATURE ? :) //may do better then to!string fns[Vars[i].stringof] = (){ app.put(to!string(v)); }; } auto slice = tmpl; while(!slice.empty) { auto anchor = find(slice, ' '); app.put(slice[0..$-anchor.length]); if(anchor.empty) break; slice = find!(a => !isAlpha(a))(anchor[1..$]); auto name = anchor[1..$-slice.length]; if(!slice.empty) { if(name in fns) fns[name](); } } return app.data; } void main(){ int a = 2; float b = 3.14; string c = "hello"; auto x = q{var= a, second= b, third= c!}.embed!(a,b,c); writeln(x); int age = 45; string address = "Fleet st. 15"; dstring fieldName = "my_field"; auto y = q{ "firstName": " firstName", "age": age, "address": { fieldName: " address", } }.embed!(age, fieldName, address); writeln(y); } Prints as expected: var=2, second=3.14, third=hello! "firstName": "", "age": 45, "address": { my_field: "Fleet st. 15", } Now isn't it already nice beyond proportions? -- Dmitry Olshansky
Nov 06 2013
parent reply "Daniel Davidson" <nospam spam.com> writes:
On Wednesday, 6 November 2013 at 21:37:14 UTC, Dmitry Olshansky 
wrote:
 07-Nov-2013 01:02, Timothee Cour пишет:
 To those who don't see the use of this:

 which code would you rather read & write? see pastebin:
 http://dpaste.dzfl.pl/b9f65a39

 Another advantage is that using an autoformatter won't mess up 
 things
 inside a r{..} literal.
Challenge accepted. 50 lines in current D2: import std.algorithm, std.conv, std.array, std.uni, std.stdio; auto embed(Vars...)(string tmpl) { auto app = appender!string(); alias Put = void delegate(); Put[string] fns; foreach(i, v; Vars) { //strangely v.stringof is "v" - BUG/FEATURE ? :) //may do better then to!string fns[Vars[i].stringof] = (){ app.put(to!string(v)); }; } auto slice = tmpl; while(!slice.empty) { auto anchor = find(slice, ' '); app.put(slice[0..$-anchor.length]); if(anchor.empty) break; slice = find!(a => !isAlpha(a))(anchor[1..$]); auto name = anchor[1..$-slice.length]; if(!slice.empty) { if(name in fns) fns[name](); } } return app.data; } void main(){ int a = 2; float b = 3.14; string c = "hello"; auto x = q{var= a, second= b, third= c!}.embed!(a,b,c); writeln(x); int age = 45; string address = "Fleet st. 15"; dstring fieldName = "my_field"; auto y = q{ "firstName": " firstName", "age": age, "address": { fieldName: " address", } }.embed!(age, fieldName, address); writeln(y); } Prints as expected: var=2, second=3.14, third=hello! "firstName": "", "age": 45, "address": { my_field: "Fleet st. 15", } Now isn't it already nice beyond proportions?
That is cool, but it is not DRY. Interpolation is well established and clearly adds value - else why would almost every language provide it? You should not have to pass (age, fieldName, address) into it - since it already has it in the string. That is the real challenge, get those variables and interpolate them. Maybe I am missing something, but the format approach advocated by you and dicebot means breaking up strings (ugly and potentially error prone) and/or repeating yourself. Thanks Dan
Nov 06 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Nov 06, 2013 at 10:50:35PM +0100, Daniel Davidson wrote:
 On Wednesday, 6 November 2013 at 21:37:14 UTC, Dmitry Olshansky
 wrote:
[...]
Challenge accepted.
50 lines in current D2:

import std.algorithm, std.conv, std.array, std.uni, std.stdio;

auto embed(Vars...)(string tmpl)
{
    auto app = appender!string();
    alias Put = void delegate();
    Put[string] fns;
    foreach(i, v; Vars)
    {
        //strangely v.stringof is "v" - BUG/FEATURE ? :)
	//may do better then to!string
        fns[Vars[i].stringof] = (){ app.put(to!string(v)); };
    }

    auto slice = tmpl;
    while(!slice.empty)
    {
        auto anchor = find(slice, ' ');
        app.put(slice[0..$-anchor.length]);
        if(anchor.empty)
            break;
        slice = find!(a => !isAlpha(a))(anchor[1..$]);
        auto name = anchor[1..$-slice.length];
        if(!slice.empty)
        {
            if(name in fns)
                fns[name]();
        }
    }
    return app.data;
}

void main(){
    int a = 2;
    float b = 3.14;
    string c = "hello";
    auto x = q{var= a, second= b, third= c!}.embed!(a,b,c);
    writeln(x);
    int age = 45;
    string address = "Fleet st. 15";
    dstring fieldName = "my_field";
    auto y = q{
      "firstName": " firstName",
      "age":  age,
      "address": {
         fieldName: " address",
      }
    }.embed!(age, fieldName, address);
    writeln(y);
}

Prints as expected:
var=2, second=3.14, third=hello!

      "firstName": "",
      "age": 45,
      "address": {
        my_field: "Fleet st. 15",
      }


Now isn't it already nice beyond proportions?
That is cool, but it is not DRY. Interpolation is well established and clearly adds value - else why would almost every language provide it? You should not have to pass (age, fieldName, address) into it - since it already has it in the string. That is the real challenge, get those variables and interpolate them. Maybe I am missing something, but the format approach advocated by you and dicebot means breaking up strings (ugly and potentially error prone) and/or repeating yourself.
[...] Challenge accepted. ;-) Here is an adaptation of Dmitri's code that doesn't require you to explicitly pass in variables: import std.algorithm; import std.array; import std.stdio; import std.uni; string interpolate(string fmt) { // The bulk of this code is copied from Dmitri's version; // thanks, Dmitri! auto app = appender!string(); app.put("\""); auto slice = fmt; while (!slice.empty) { auto anchor = find(slice, ' '); // Escape unsafe characters foreach(c; slice[0..$-anchor.length]) { if (c == '\"') app.put("\\\""); else app.put(c); } if (anchor.empty) break; slice = find!(a => !isAlpha(a))(anchor[1..$]); auto name = anchor[1..$-slice.length]; if (!slice.empty) { app.put("\"~" ~ name ~ "~\""); } } app.put("\""); return app.data; } void main() { string a = "aye"; string b = "bee"; string c = "cee"; writeln(mixin(interpolate(q{ "myobject" : { "fieldA": " a", "fieldB": " b", "fieldC": " c", } }))); } Here's the output: "myobject" : { "fieldA": "aye", "fieldB": "bee", "fieldC": "cee", } Is that acceptable to you? :) Of course, the above code is just a proof-of-concept; it doesn't handle integer or other types of variables, and it currently only escapes '"', (it should be extended to also escape '\', etc.). But all of these would be easily addressed by a proper implementation using std.conv and by handling metacharacters properly. The point is that you *can* do string interpolation in D without needing language-level support. T -- May you live all the days of your life. -- Jonathan Swift
Nov 06 2013
parent "Daniel Davidson" <nospam spam.com> writes:
On Wednesday, 6 November 2013 at 22:33:36 UTC, H. S. Teoh wrote:
 Challenge accepted. ;-) Here is an adaptation of Dmitri's code 
 that
 doesn't require you to explicitly pass in variables:
...
 Is that acceptable to you? :)
Good stuff.
 Of course, the above code is just a proof-of-concept; it 
 doesn't handle
 integer or other types of variables, and it currently only 
 escapes '"',
 (it should be extended to also escape '\', etc.). But all of 
 these would
 be easily addressed by a proper implementation using std.conv 
 and by
 handling metacharacters properly. The point is that you *can* 
 do string
 interpolation in D without needing language-level support.
Absolutely - I was not under the impression it needed to be provided by the language proper. But a standard library would be nice. I look forward to seeing/using Timothy's version when it is ready. Lack of interpolation is one reason I went with Dart for my code generation tasks.
Nov 06 2013
prev sibling parent reply Timothee Cour <thelastmammoth gmail.com> writes:
On Wed, Nov 6, 2013 at 2:32 PM, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:

 On Wed, Nov 06, 2013 at 10:50:35PM +0100, Daniel Davidson wrote:
 On Wednesday, 6 November 2013 at 21:37:14 UTC, Dmitry Olshansky
 wrote:
[...]
Challenge accepted.
50 lines in current D2:

import std.algorithm, std.conv, std.array, std.uni, std.stdio;

auto embed(Vars...)(string tmpl)
{
    auto app = appender!string();
    alias Put = void delegate();
    Put[string] fns;
    foreach(i, v; Vars)
    {
        //strangely v.stringof is "v" - BUG/FEATURE ? :)
     //may do better then to!string
        fns[Vars[i].stringof] = (){ app.put(to!string(v)); };
    }

    auto slice = tmpl;
    while(!slice.empty)
    {
        auto anchor = find(slice, ' ');
        app.put(slice[0..$-anchor.length]);
        if(anchor.empty)
            break;
        slice = find!(a => !isAlpha(a))(anchor[1..$]);
        auto name = anchor[1..$-slice.length];
        if(!slice.empty)
        {
            if(name in fns)
                fns[name]();
        }
    }
    return app.data;
}

void main(){
    int a = 2;
    float b = 3.14;
    string c = "hello";
    auto x = q{var= a, second= b, third= c!}.embed!(a,b,c);
    writeln(x);
    int age = 45;
    string address = "Fleet st. 15";
    dstring fieldName = "my_field";
    auto y = q{
      "firstName": " firstName",
      "age":  age,
      "address": {
         fieldName: " address",
      }
    }.embed!(age, fieldName, address);
    writeln(y);
}

Prints as expected:
var=2, second=3.14, third=hello!

      "firstName": "",
      "age": 45,
      "address": {
        my_field: "Fleet st. 15",
      }


Now isn't it already nice beyond proportions?
That is cool, but it is not DRY. Interpolation is well established and clearly adds value - else why would almost every language provide it? You should not have to pass (age, fieldName, address) into it - since it already has it in the string. That is the real challenge, get those variables and interpolate them. Maybe I am missing something, but the format approach advocated by you and dicebot means breaking up strings (ugly and potentially error prone) and/or repeating yourself.
[...] Challenge accepted. ;-) Here is an adaptation of Dmitri's code that doesn't require you to explicitly pass in variables: import std.algorithm; import std.array; import std.stdio; import std.uni; string interpolate(string fmt) { // The bulk of this code is copied from Dmitri's version; // thanks, Dmitri! auto app = appender!string(); app.put("\""); auto slice = fmt; while (!slice.empty) { auto anchor = find(slice, ' '); // Escape unsafe characters foreach(c; slice[0..$-anchor.length]) { if (c == '\"') app.put("\\\""); else app.put(c); } if (anchor.empty) break; slice = find!(a => !isAlpha(a))(anchor[1..$]); auto name = anchor[1..$-slice.length]; if (!slice.empty) { app.put("\"~" ~ name ~ "~\""); } } app.put("\""); return app.data; } void main() { string a = "aye"; string b = "bee"; string c = "cee"; writeln(mixin(interpolate(q{ "myobject" : { "fieldA": " a", "fieldB": " b", "fieldC": " c", } }))); } Here's the output: "myobject" : { "fieldA": "aye", "fieldB": "bee", "fieldC": "cee", } Is that acceptable to you? :) Of course, the above code is just a proof-of-concept; it doesn't handle integer or other types of variables, and it currently only escapes '"', (it should be extended to also escape '\', etc.). But all of these would be easily addressed by a proper implementation using std.conv and by handling metacharacters properly. The point is that you *can* do string interpolation in D without needing language-level support.
As I mentioned in my OT, this is what I already have implemented ("I've actually already implemented this feature via a mixin, and find it extremely useful, but ... "); and I did take care of proper escaping, etc. I am using it extensively, however the requirement for using a mixin makes things uglier than they should: * cryptic ctfe error msgs upon wrong variable names * mixin can't be used in UFCS chains; additional () nesting to the point that I only use it in cases where the alternative is uglier. * potentially more strain on ctfe * mixins in general should be used sparingly Sure we can use existing mixins to fill this need, but to me this is exactly the same as the situation with lambda literal syntax: a=>a*2 instead of (a){return a*2;} or lazy parameters: void fun(lazy string a) vs: void fun(string delegate() a) A little of syntax sugar can provide huge benifits. It's use case would apply to all assert error messages, DSLs etc.
 T

 --
 May you live all the days of your life. -- Jonathan Swift
Nov 06 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-11-06 23:48, Timothee Cour wrote:

 As I mentioned in my OT, this is what I already have implemented ("I've
 actually already implemented this feature via a mixin, and find it
 extremely useful, but ... "); and I did take care of proper escaping,
 etc. I am using it extensively, however the requirement for using a
 mixin makes things uglier than they should:

 * cryptic ctfe error msgs upon wrong variable names
 * mixin can't be used in UFCS chains; additional () nesting
 to the point that I only use it in cases where the alternative is uglier.
 * potentially more strain on ctfe
 * mixins in general should be used sparingly

 Sure we can use existing mixins to fill this need, but to me this is
 exactly the same as the situation with lambda literal syntax:

 a=>a*2
 instead of
 (a){return a*2;}

 or lazy parameters:
 void fun(lazy string a)
 vs:
 void fun(string delegate() a)

 A little of syntax sugar can provide huge benifits.
 It's use case would apply to all assert error messages, DSLs etc.
I agree with you, but I rather have a more general solution. That is, AST macros. -- /Jacob Carlborg
Nov 07 2013
parent reply Timothee Cour <thelastmammoth gmail.com> writes:
On Thu, Nov 7, 2013 at 2:27 AM, Jacob Carlborg <doob me.com> wrote:

 On 2013-11-06 23:48, Timothee Cour wrote:

  As I mentioned in my OT, this is what I already have implemented ("I've
 actually already implemented this feature via a mixin, and find it
 extremely useful, but ... "); and I did take care of proper escaping,
 etc. I am using it extensively, however the requirement for using a
 mixin makes things uglier than they should:

 * cryptic ctfe error msgs upon wrong variable names
 * mixin can't be used in UFCS chains; additional () nesting
 to the point that I only use it in cases where the alternative is uglier.
 * potentially more strain on ctfe
 * mixins in general should be used sparingly

 Sure we can use existing mixins to fill this need, but to me this is
 exactly the same as the situation with lambda literal syntax:

 a=>a*2
 instead of
 (a){return a*2;}

 or lazy parameters:
 void fun(lazy string a)
 vs:
 void fun(string delegate() a)

 A little of syntax sugar can provide huge benifits.
 It's use case would apply to all assert error messages, DSLs etc.
I agree with you, but I rather have a more general solution. That is, AST macros.
I would love to have AST macros too, but how do they help for the problem at hand? (eg use cases in OT + DSL). Like others mentioned, the feature is not crazy, it is present in most languages.
 --
 /Jacob Carlborg
Nov 07 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-11-07 12:05, Timothee Cour wrote:

 I would love to have AST macros too, but how do they help for the
 problem at hand? (eg use cases in OT + DSL). Like others mentioned, the
 feature is not crazy, it is present in most languages.
If AST macros worked like my vision [1] then it can be done by doing something like this. The declaration of the macro would look like this: macro interpolate (Context context, Ast!(string) str) * Scan "str" for occurrences of $variableName and $(expression) or whatever syntax is chosen * Replace all occurrences with "%s" * Create the following AST: format(replacedStr, variableName, expression); Use it like: int a = 3; int b = 4; auto str = interpolate("a=$a b=$b"); Will generate the same AST as: auto str = format("a=%s b=%s", a, b); [1] https://dl.dropboxusercontent.com/u/18386187/ast_macros.html -- /Jacob Carlborg
Nov 07 2013