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