DIP idea: q{}-inspired block mixins

mw mingwu at gmail.com
Wed Jun 10 04:03:46 UTC 2020


On Wednesday, 10 June 2020 at 03:03:49 UTC, Q. Schroll wrote:
> Suggestion:
>
>     mixin[op]
>     {
>         lhs op= rhs;
>     }
> ...
> evaluated (mixed in). So the above is equivalent to:
>
>     mixin("lhs ", op, "= rhs;");
>
> For a simple idea(*): Pack everything between the braces in a 
> q{} string. "Interrupt" the q{} string anywhere an identifier 
> `ident` of the bracketed list is found by "},ident,q{".
> (*) Doesn't work when braces are involved.
>
> A prime example is pseudo-code we have on the spec that doesn't 
> compile. We often write stuff like op= and expect people to 
> interpret it properly. With that, the compiler could, too.
>
> FAQ.
>
> Q: Can I use multiple identifiers?
> A: Yes. Comma-separated in the brackets. E.g. [ident1, ident2]

I like it. Actually I was thinking about it since my previous 
question and answer from Ali:

https://forum.dlang.org/post/ravtsk$1uav$1@digitalmars.com

The reason that template is so hard to be write tidily, is 
because D does not have an easy way to stringfy a token.

As a D beginner, I can come-up with this simple version:
https://forum.dlang.org/post/cggmhdgmsxsutangohlf@forum.dlang.org

--------------------
enum RW(string T, string name) =
   format(q{
     private %1$s _%2$s;
     public  %1$s  %2$s()        {return _%2$s;}
     public  auto  %2$s(%1$s v)  {_%2$s = v;  return this;}
   }, T, name);


class Point {
   mixin(RW!("int",     "x"));
   mixin(RW!("double",  "y"));
   mixin(RW!("string",  "z"));
}
--------------------

although it got the job done, but it's ugly:
1) in the implementation, string format and format marker (%1$s, 
_%2$s, etc.) all over the place.
2) from the mixin call-site, string are passed (mixin(RW!("int", 
"x"))), instead of just simple token.

Other people (esp. Ali) tried to work around this problem by 
using template parameter T for type, and dispatch(name) to 
stringfy that token. It is the only way to do it now in D, and it 
took an D language expert (Ali, book author) quite sometime to 
achieve this:

   mixin RW!int.x;    // <-- NICE :)

in his second attempt. And even he said it's "convoluted".

The language shouldn't be made it so hard to achieve something 
this simple, e.g. a novice C++ programmer can write this 
equivalent thing without too much effort:

-----------------------------
#define ATTR_NAME(name)  _##name

#define READER(type, name)      \
public:    type name () const {return ATTR_NAME(name);}

#define WRITER(type, name)      \
public:  void name(type val)  {ATTR_NAME(name) = val;}

#define READ_ONLY_ATTR(type, name)      \
protected: type ATTR_NAME(name);        \
            READER(type, name)

#define DECL_ATTR(type, name)   \
    READ_ONLY_ATTR(type, name)   \
            WRITER(type, name)

class Point {
   DECL_ATTR(int, x);
   DECL_ATTR(int, y);
   DECL_ATTR(int, z);
};
-----------------------------

And do you think this simple C++ macro version is much more 
easily readable & maintainable than the D expert's version: 
(copied from: 
https://forum.dlang.org/post/ravtsk$1uav$1@digitalmars.com)

-----------------------------
Ok, I solved that too with a very convoluted "eponymous mixin 
template opDispatch." :)

struct RW(T) {
   template opDispatch(string name) {
     static codeImpl() {
       import std.format;

       return format!q{
         private %s _%s;
         public auto %s() { return _%s; }
         public auto %s(%s val) { _%s = val; return this; }
       }(T.stringof, name,
         name, name,
         name, T.stringof, name);
     }

     mixin template opDispatch(alias code = codeImpl()) {
       mixin (code);
     }
   }
}

struct Point {
   mixin RW!int.x;    // <-- NICE :)
   mixin RW!int.y;
}

-----------------------------

Actually opDispatch plays the role of stringfy a token already, 
so *why not* just give it a proper mechanism of its own? 
Otherwise, people are enforced to write such *convoluted* code to 
be able to use it to stringfy a token.

In short, we have the mechanism already (thru opDispatch(...)), 
but please export it as first class D citizen.

I'm glad you made this proposal, and I hope it will be adopted.




More information about the Digitalmars-d mailing list