www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP idea: q{}-inspired block mixins

reply Q. Schroll <qs.il.paperinik gmail.com> writes:
Mixin declarations and statements are often used in conjunction 
with large literals containing lots of code, where stuff is 
inserted at specific points using interruptions like mixin("...", 
identifier, "...") or %s format specifiers and .format afterwards.
It hurts readability. In the best case, code should directly 
express what we mean.
We mean in those cases: Insert the value of identifier and not 
"identifier" here.
An observation is also that hardly anywhere, the value of the 
identifier and not "identifier" are used together.

Suggestion:

     mixin[op]
     {
         lhs op= rhs;
     }

It parses the stuff between the { and } the same as a q{}-string, 
but any identifier token found that is identical to the one 
between [ and ] will not be used literally, but be 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]

Q: Can I use expressions?
A: Instead of a plain identifier, use [ident = expression()].

Q: So [ident] alone is equivalent to [ident = ident]?
A: You got the idea right, but you cannot actually write that. 
Use [ident] or [ident2 = ident].

Q: Can I mix assignments and single identifiers?
A: Sure.

Q: Where can I use it?
A: Anywhere a mixin() statement or declaration can be. It's just 
an alternative syntax to mixin per se. Potentially also 
everywhere a {}-block can be. (Let's see what others think about 
that.)

Q: Wait, what about mixin() expressions?
A: Unfortunately not those. Block mixins are for complete 
statements or delcarations (depending on the context). Note that 
the examples didn't use a semicolon at their end.

Q: Can I use the identifiers in [ and ] in the block mixin 
without being mixed in (i.e. "escaped" in some sense)?
A: No. Use [ident2 = ident]. Then ident will not be replaced by 
its value.

Syntax up for debate.

Disclaimer: I don't really know how q{} strings are lexed and 
parsed. I'd assume the they're lexed as separate tokens, parsed 
token-wise until the closing brace is found.

What do you think about it?
Jun 09
next sibling parent reply mw <mingwu gmail.com> writes:
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 #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.
Jun 09
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 10 June 2020 at 04:03:46 UTC, mw wrote:
 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);
Please don't do `string T`, typeName would be way better. Actually, my proposal doesn't improve that in length, but you could do // Assuming typeName and name in scope: mixin[typeName, name, _name = '_' ~ name] { private typeName _name; public typeName name() { return _name; } public ref name(return typeName value) { _name = value; return value; } } In contrast to format() approaches, it at least can be readable. At the points you're doing these:
 class Point {
   mixin(RW!("int",     "x"));
   mixin(RW!("double",  "y"));
   mixin(RW!("string",  "z"));
 }
you'd need to copy the parts from above over and over. There's no (easy) way to make the stuff in the mixin[]{} a string literal that can be mixed in without resorting to, well, format() again. You could keep it to the []-list at least: enum RW(string typeName, string myname) = " mixin[tName = \"%s\", name = \"%s\", _name = \"_%s\"] { ... } ".format(typeName, myname, myname); mixin(RW!("int", "x")); ... I do think it's an improvement.
 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

 #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?
I think the C++ macro does a good job here. Macros are not evil, just easy to misuse. C++-Macros have a ton of issues, but the mere idea of token-based replacement is not bad at all.
 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;
 }
I like that approach, too. So much better than the above! Yes, here, in the opDispatch, my proposal wouldn't shorten anything (it would never do this), but make it much more readable.
 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.
I'd also hope it flies.
Jun 10
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 10 June 2020 at 23:51:45 UTC, Q. Schroll wrote:

About this:

     enum RW(string typeName, string myname) = "
         mixin[tName = \"%s\", name = \"%s\", _name = \"_%s\"]
         {
             ...
         }
     ".format(typeName, myname, myname);
It shouldn't be a template but rather a function. (For Stefan)
Jun 10
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 11 June 2020 at 00:20:35 UTC, Q. Schroll wrote:
 On Wednesday, 10 June 2020 at 23:51:45 UTC, Q. Schroll wrote:

 About this:

     enum RW(string typeName, string myname) = "
         mixin[tName = \"%s\", name = \"%s\", _name = \"_%s\"]
         {
             ...
         }
     ".format(typeName, myname, myname);
It shouldn't be a template but rather a function. (For Stefan)
std.format.format is mighty expensive ;) your proposal would make that a built-in right?
Jun 11
parent Q. Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 11 June 2020 at 13:20:43 UTC, Stefan Koch wrote:
 On Thursday, 11 June 2020 at 00:20:35 UTC, Q. Schroll wrote:
 On Wednesday, 10 June 2020 at 23:51:45 UTC, Q. Schroll wrote:

 About this:

     enum RW(string typeName, string myname) = "
         mixin[tName = \"%s\", name = \"%s\", _name = \"_%s\"]
         {
             ...
         }
     ".format(typeName, myname, myname);
It shouldn't be a template but rather a function. (For Stefan)
std.format.format is mighty expensive ;) your proposal would make that a built-in right?
Not really. What format does here is putting strings together. My proposal never intended to splice in numeric or other things.
Jun 20
prev sibling next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 10 June 2020 at 03:03:49 UTC, Q. Schroll wrote:

I like it, but I have some Qs for a Q

 Q: Can I use expressions?
 A: Instead of a plain identifier, use [ident = expression()].
Why expression()? I.e. why parentheses? Evaluated how many times? Eagerly once? Lazily every time 'ident' is encountered within the {} ? Lazily once, the first time 'ident' is encountered within the {}?
Jun 10
parent Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 10 June 2020 at 12:49:47 UTC, Stanislav Blinov 
wrote:
 On Wednesday, 10 June 2020 at 03:03:49 UTC, Q. Schroll wrote:

 I like it, but I have some Qs for a Q

 Q: Can I use expressions?
 A: Instead of a plain identifier, use [ident = expression()].
Why expression()? I.e. why parentheses?
To indicate an evaluation may take place. I thought it were more understandable than without.
 Evaluated how many times? Eagerly once? Lazily every time 
 'ident' is encountered within the {} ? Lazily once, the first 
 time 'ident' is encountered within the {}?
I'd suggest every assignment eagerly once. That's the version that seems the least confusing in most cases. Imagine it like this: enum __ident = ident; or enum __ident = expression(); depending on which one is used, and splicing "...", __ident, "..." into the parameters to regular mixin(). The same is done in foreach loops to the range expression; in foreach (element; range()), (range() is an expression), the range expression is evaluated once eagerly. Maybe, if you (or someone else) can make a compelling point to add `lazy ident = expr()` for the case where it should be evaluated lazily every time it's spliced in, I'd put it in the DIP. Also: Q: Why brackets? A: Because any other syntax that came to my mind is either less readable or entails code breakage.
Jun 10
prev sibling next sibling parent Luis <luis.panadero gmail.com> writes:
On Wednesday, 10 June 2020 at 03:03:49 UTC, Q. Schroll wrote:
 Mixin declarations and statements are often used in conjunction 
 with large literals containing lots of code, where stuff is 
 inserted at specific points using interruptions like 
 mixin("...", identifier, "...") or %s format specifiers and 
 .format afterwards.
 It hurts readability. In the best case, code should directly 
 express what we mean.
 We mean in those cases: Insert the value of identifier and not 
 "identifier" here.
 An observation is also that hardly anywhere, the value of the 
 identifier and not "identifier" are used together.

 Suggestion:

     mixin[op]
     {
         lhs op= rhs;
     }

 It parses the stuff between the { and } the same as a 
 q{}-string, but any identifier token found that is identical to 
 the one between [ and ] will not be used literally, but be 
 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]

 Q: Can I use expressions?
 A: Instead of a plain identifier, use [ident = expression()].

 Q: So [ident] alone is equivalent to [ident = ident]?
 A: You got the idea right, but you cannot actually write that. 
 Use [ident] or [ident2 = ident].

 Q: Can I mix assignments and single identifiers?
 A: Sure.

 Q: Where can I use it?
 A: Anywhere a mixin() statement or declaration can be. It's 
 just an alternative syntax to mixin per se. Potentially also 
 everywhere a {}-block can be. (Let's see what others think 
 about that.)

 Q: Wait, what about mixin() expressions?
 A: Unfortunately not those. Block mixins are for complete 
 statements or delcarations (depending on the context). Note 
 that the examples didn't use a semicolon at their end.

 Q: Can I use the identifiers in [ and ] in the block mixin 
 without being mixed in (i.e. "escaped" in some sense)?
 A: No. Use [ident2 = ident]. Then ident will not be replaced by 
 its value.

 Syntax up for debate.

 Disclaimer: I don't really know how q{} strings are lexed and 
 parsed. I'd assume the they're lexed as separate tokens, parsed 
 token-wise until the closing brace is found.

 What do you think about it?
This and/or string interpolation would writing mixins more comfortable. Another good point that have this solution, its that makes mixin code highlighted and IDEs could be his magic (autocomplete, refactor, etc) with mixing code.
Jun 11
prev sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
Here is the DIP PR: https://github.com/dlang/DIPs/pull/194 and 
the DIP document: 
https://github.com/Bolpat/DIPs/blob/TokenMixins/DIPs/DIP-1NN2-QFS.md

Please let me know of any suggestions that come to your mind in 
this thread or the PR discussion.
Nov 04
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 5 November 2020 at 04:07:06 UTC, Q. Schroll wrote:
 Here is the DIP PR: https://github.com/dlang/DIPs/pull/194 and 
 the DIP document: 
 https://github.com/Bolpat/DIPs/blob/TokenMixins/DIPs/DIP-1NN2-QFS.md

 Please let me know of any suggestions that come to your mind in 
 this thread or the PR discussion.
This seems like an extremely narrow and special-purpose feature for something that could just as easily be addressed by a more general feature, like string interpolation or quasiquotation. It also shares the same basic weakness as format strings, which is that every time you see (for example) an `op` in the mixin body, you have to look somewhere else in the code to see what it's going to be replaced with. And unlike a `%s` format specifier, there's no easy way to distinguish a replacement token in the mixin body from a normal identifier.
Nov 04
parent Jacob Carlborg <doob me.com> writes:
On Thursday, 5 November 2020 at 06:04:47 UTC, Paul Backus wrote:

 This seems like an extremely narrow and special-purpose feature 
 for something that could just as easily be addressed by a more 
 general feature, like string interpolation or quasiquotation.
I fully agree.
 It also shares the same basic weakness as format strings, which 
 is that every time you see (for example) an `op` in the mixin 
 body, you have to look somewhere else in the code to see what 
 it's going to be replaced with. And unlike a `%s` format 
 specifier, there's no easy way to distinguish a replacement 
 token in the mixin body from a normal identifier.
I agree. But that's also how templates behave and I have not heard of anyone complaining about that. -- /Jacob Carlborg
Nov 05
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On Thursday, 5 November 2020 at 04:07:06 UTC, Q. Schroll wrote:
 Here is the DIP PR: https://github.com/dlang/DIPs/pull/194 and 
 the DIP document: 
 https://github.com/Bolpat/DIPs/blob/TokenMixins/DIPs/DIP-1NN2-QFS.md

 Please let me know of any suggestions that come to your mind in 
 this thread or the PR discussion.
Isn't it quite rare to have the mixin location and the string, to be mixed in, at the same place? For example: mixin("int a = 3;"); // rare string generate(string name) { return "int " ~ name " = 3;"; // code is here } mixin(generate("a")); // mixin location is here, more common How will your solution help with that? Perhaps we can turn the problem around. Instead of doing some form of highly specialized string interpolation, that only works in one place, instead focus on removing the need to use strings in the first place. In the cases I had the need to use a string mixins, most of the code would have worked with a template mixin, but it was just the identifier that caused the need to move to string mixins. Example: mixin template property(T) { private T a_; T a() { return a_; } } class Foo { mixin property!int; } To be able to specify the name of the property the whole template mixin needs to be replaced with a string mixin. What if we instead could allow string mixins in more locations. Example: mixin template property(T, string name) { private T mixin(name)_; T mixin(name)() { return mixin(name)_; } } class Foo { mixin property!(int, "a"); } I would not call the above a new language feature, just removing some limitations. Or, the solution to all of the above (and many more issues): AST transformations [1]. [1] https://wiki.dlang.org/DIP50 -- /Jacob Carlborg
Nov 05
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 5 November 2020 at 12:51:38 UTC, Jacob Carlborg 
wrote:
 Isn't it quite rare to have the mixin location and the string, 
 to be mixed in, at the same place? For example:
That's probably the majority of my uses and a lot of times it is specifically to use the name of a declaration with most everything else being inside the static string.
Nov 05
prev sibling next sibling parent data pulverizer <data.pulverizer gmail.com> writes:
On Thursday, 5 November 2020 at 12:51:38 UTC, Jacob Carlborg 
wrote:
 Perhaps we can turn the problem around. Instead of doing some 
 form of highly specialized string interpolation, that only 
 works in one place, instead focus on removing the need to use 
 strings in the first place.

 In the cases I had the need to use a string mixins, most of the 
 code would have worked with a template mixin, but it was just 
 the identifier that caused the need to move to string mixins. 
 Example:

 mixin template property(T)
 {
     private T a_;
     T a() { return a_; }
 }

 class Foo
 {
     mixin property!int;
 }

 To be able to specify the name of the property the whole 
 template mixin needs to be replaced with a string mixin. What 
 if we instead could allow string mixins in more locations. 
 Example:

 mixin template property(T, string name)
 {
     private T mixin(name)_;
     T mixin(name)() { return mixin(name)_; }
 }

 class Foo
 {
     mixin property!(int, "a");
 }

 I would not call the above a new language feature, just 
 removing some limitations.

 Or, the solution to all of the above (and many more issues): 
 AST transformations [1].

 [1] https://wiki.dlang.org/DIP50

 --
 /Jacob Carlborg
Something like the code below seems innocuous to me and is not allowed in template mixins. ``` mixin template loop1(long n, T = double) { T x = 0; for(long i = 0; i < n; ++i) { x += cast(T)(i)^^2.0; } } ``` Maybe there is a case for relaxing the rules on for, while, and so on?
Nov 05
prev sibling next sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 5 November 2020 at 12:51:38 UTC, Jacob Carlborg 
wrote:
 Isn't it quite rare to have the mixin location and the string, 
 to be mixed in, at the same place?
I'd say mixin(generate(args)) and mixin("..pattern..", hole, "..pattern..") are currently the two standard uses of string mixins. While the first is perfectly fine, the latter lacks readability greatly, since **code to be read is in string literals** where it shouldn't be. In the first use-case, generate may stitch together the generated code in a way that might be hard to follow, but that's hard to improve. My proposal makes code, well, code. Interpolated strings don't help that either. Interpolated strings' mission is to generate strings which has its merits and use-cases well beyond string mixins. To convince myself I've just run grep 'mixin(.*"' *.d and grep 'mixin.*q{' *.d over Phobos (std/) and got 169 and 10 results respectively. (This search is surface-level but finds good candidates.) I reviewed every one of them manually and found 133 of 169 and 8 of 10 would probably have been written with my proposed syntax. Since Phobos is a meta-programming heavy library, I'd consider it a representative target, as mixin is without doubt a meta-programming tool. To be fair, 78 of the 133 matches follow one of the operator mixin patterns ("lhs" ~ op ~ "rhs" or op~"value") which is probably the most common and obvious use-case.
Nov 05
parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Friday, 6 November 2020 at 00:50:16 UTC, Q. Schroll wrote:
 On Thursday, 5 November 2020 at 12:51:38 UTC, Jacob Carlborg 
 wrote:
 Isn't it quite rare to have the mixin location and the string, 
 to be mixed in, at the same place?
I'd say mixin(generate(args)) and mixin("..pattern..", hole, "..pattern..") are currently the two standard uses of string mixins. While the first is perfectly fine, the latter lacks readability greatly, since **code to be read is in string literals** where it shouldn't be. In the first use-case, generate may stitch together the generated code in a way that might be hard to follow, but that's hard to improve. My proposal makes code, well, code. Interpolated strings don't help that either. Interpolated strings' mission is to generate strings which has its merits and use-cases well beyond string mixins. To convince myself I've just run grep 'mixin(.*"' *.d and grep 'mixin.*q{' *.d over Phobos (std/) and got 169 and 10 results respectively. (This search is surface-level but finds good candidates.) I reviewed every one of them manually and found 133 of 169 and 8 of 10 would probably have been written with my proposed syntax. Since Phobos is a meta-programming heavy library, I'd consider it a representative target, as mixin is without doubt a meta-programming tool. To be fair, 78 of the 133 matches follow one of the operator mixin patterns ("lhs" ~ op ~ "rhs" or op~"value") which is probably the most common and obvious use-case.
I like your DIP. It's one of those simple syntax changes that have a huge effect on readability without introducing complex concepts. It is also not that uncommon intellectual concept. It is similar to a with(e) {} or catch(e) {}. People who object to your DIP for "difficulty to make the link between expression in [] and the statements" reasons, should be high up in arms against with statement, as there the readability is worse as there is not even a hint where the head expression was omitted. You should add these statistics to the dip, to bring home the point. Choose 1 or 2 (or 3) specially juicy examples from that list showing the undeniable readability advantage it brings.
Nov 05
prev sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 5 November 2020 at 12:51:38 UTC, Jacob Carlborg 
wrote:
 To be able to specify the name of the property the whole 
 template mixin needs to be replaced with a string mixin. What 
 if we instead could allow string mixins in more locations?
Something like that has been proposed before, called "mixin identifiers"; everywhere an Identifier would be valid, one could use a string mixin to generate and splice in the identifier. Of your example, mixin template property(T, string name) { private T mixin(name)_; T mixin(name)() { return mixin(name)_; } // ----------- } only the highlighted part would word. The other parts don't make sense; mixins don't "connect" with surrounding tokens. If `name` is "prop", `mixin(name)_` cannot become `prop_`. No kind of mixin does that; not even my DIP proposes that. Borrowing from your example, you'd do: mixin template property(T, string name) { mixin[name, name_ = name ~ "_"] { private T name_; T name() { return name_; } } } which reads very well, I guess. I'm not opposed to mixin identifiers, since they tackle a not-so-rare use-case. Another proposal was being able to mix in the name of something that's being declared. In your example, that would be the variable name_ and the function name(), but it wouldn't tackle `return name_;`. I'd call it mixin declarations, but that name is already taken by string mixins that are in a declarative scope (module, struct, union, class, interface, template). Mixin identifiers would e.g. allow you to mix in parameter type names and parameter names. Since isn't part of an attribute, mixin(..) would also work, since the grammar says Identifier is valid. (Funny enough, " safe" [note the space] works perfectly, but no-one uses it.) Maybe I'll make Mixin Identifiers part of the DIP. Let me know what you think.
Nov 05
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 6 November 2020 at 01:06:28 UTC, Q. Schroll wrote:
 Something like that has been proposed before, called "mixin 
 identifiers"; everywhere an Identifier would be valid, one 
 could use a string mixin to generate and splice in the 
 identifier. Of your example,

 mixin template property(T, string name)
 {
     private T mixin(name)_;
     T mixin(name)() { return mixin(name)_; }
 //    -----------
 }

 only the highlighted part would word. The other parts don't 
 make sense; mixins don't "connect" with surrounding tokens. If 
 `name` is "prop", `mixin(name)_` cannot become `prop_`. No kind 
 of mixin does that; not even my DIP proposes that.
This is trivially fixed by changing `mixin(name)_` to `mixin(name, "_")`.
 Maybe I'll make Mixin Identifiers part of the DIP. Let me know 
 what you think.
Better to make them two separate DIPs. They're completely independent features, after all.
Nov 05