proposal: a new string litteral to embed variables in a string

Timothee Cour thelastmammoth at gmail.com
Wed Nov 6 14:48:17 PST 2013


On Wed, Nov 6, 2013 at 2:32 PM, H. S. Teoh <hsteoh at 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20131106/76084eb2/attachment-0001.html>


More information about the Digitalmars-d mailing list