digitalmars.D - DIP idea: q{}-inspired block mixins
- Q. Schroll (55/55) Jun 09 2020 Mixin declarations and statements are often used in conjunction
- mw (90/108) Jun 09 2020 I like it. Actually I was thinking about it since my previous
- Q. Schroll (34/107) Jun 10 2020 Please don't do `string T`, typeName would be way better.
- Q. Schroll (3/9) Jun 10 2020 It shouldn't be a template but rather a function. (For Stefan)
- Stefan Koch (3/12) Jun 11 2020 std.format.format is mighty expensive ;)
- Q. Schroll (3/18) Jun 20 2020 Not really. What format does here is putting strings together. My
- Stanislav Blinov (6/8) Jun 10 2020 Why expression()? I.e. why parentheses?
- Q. Schroll (22/30) Jun 10 2020 To indicate an evaluation may take place. I thought it were more
- Luis (6/62) Jun 11 2020 This and/or string interpolation would writing mixins more
- Q. Schroll (5/5) Nov 04 2020 Here is the DIP PR: https://github.com/dlang/DIPs/pull/194 and
- Paul Backus (10/15) Nov 04 2020 This seems like an extremely narrow and special-purpose feature
- Jacob Carlborg (6/15) Nov 05 2020 I agree. But that's also how templates behave and I have not
- Jacob Carlborg (45/50) Nov 05 2020 Isn't it quite rare to have the mixin location and the string, to
- Adam D. Ruppe (5/7) Nov 05 2020 That's probably the majority of my uses and a lot of times it is
- data pulverizer (16/53) Nov 05 2020 Something like the code below seems innocuous to me and is not
- Q. Schroll (26/28) Nov 05 2020 I'd say mixin(generate(args)) and mixin("..pattern..", hole,
- Patrick Schluter (13/42) Nov 05 2020 I like your DIP. It's one of those simple syntax changes that
- Q. Schroll (39/42) Nov 05 2020 Something like that has been proposed before, called "mixin
- Paul Backus (5/21) Nov 05 2020 This is trivially fixed by changing `mixin(name)_` to
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 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 #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 2020
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 2020
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 2020
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:std.format.format is mighty expensive ;) your proposal would make that a built-in right?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 11 2020
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:Not really. What format does here is putting strings together. My proposal never intended to splice in numeric or other things.On Wednesday, 10 June 2020 at 23:51:45 UTC, Q. Schroll wrote: About this:std.format.format is mighty expensive ;) your proposal would make that a built-in right?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 20 2020
On Wednesday, 10 June 2020 at 03:03:49 UTC, Q. Schroll wrote: I like it, but I have some Qs for a QQ: 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 2020
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 QTo indicate an evaluation may take place. I thought it were more understandable than without.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 {}?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 2020
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 2020
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 2020
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 2020
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 2020
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 2020
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 2020
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 CarlborgSomething 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 2020
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 2020
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: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.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 2020
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 2020
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 2020