www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - mixin macros -- so tantalizing

reply Don Clugston <dac nospam.com.au> writes:
With the q{ } token strings we can now do string mixins and get syntax 
highlighting back. It's ugly, though:

mixin(foo(q{a+b*c}));

If we just had something which behaved as

#define func(X) mixin(foo(q{X}))

for example, something like:

mixin alias foo func;

which would declare func as a 'mixin alias' of foo.

(of course, there's the 'macro' keyword as well; 'abstract' is also
interesting).
allowing us to write:

func(a+b*c);

which would be instantly applicable. Almost all of the ugliness would disappear 
from my BLADE code, for example.
By contrast, I still haven't seen any use cases for the macros as described in 
the conference presentation!
Sep 07 2007
next sibling parent "Craig Black" <cblack ara.com> writes:
 for example, something like:

 mixin alias foo func;

 which would declare func as a 'mixin alias' of foo.

 (of course, there's the 'macro' keyword as well; 'abstract' is also 
 interesting).
 allowing us to write:

 func(a+b*c);

 which would be instantly applicable. Almost all of the ugliness would 
 disappear from my BLADE code, for example.
 By contrast, I still haven't seen any use cases for the macros as 
 described in the conference presentation!

You've got my vote. This sounds like a great feature that could be implemented easily. Because it complements D features that already exist, you get a lot of capability for a little effort. -Craig
Sep 07 2007
prev sibling next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Don Clugston wrote:
 With the q{ } token strings we can now do string mixins and get syntax
 highlighting back. It's ugly, though:
 
 mixin(foo(q{a+b*c}));
 
 If we just had something which behaved as
 
 #define func(X) mixin(foo(q{X}))
 
 for example, something like:
 
 mixin alias foo func;
 
 which would declare func as a 'mixin alias' of foo.
 
 (of course, there's the 'macro' keyword as well; 'abstract' is also
 interesting).
 allowing us to write:
 
 func(a+b*c);
 
 which would be instantly applicable. Almost all of the ugliness would
 disappear from my BLADE code, for example.
 By contrast, I still haven't seen any use cases for the macros as
 described in the conference presentation!

I was under the impression that *sound goes batsh!t, machine crashes; had to reboot, now where was I?* we'd be able to do this: macro func(e) { mixin(foo(e.stringof)); } func(a+b*c); I agree that your method is cleaner, however. That said, you could probably write a macro that took your ctfe function and turned it into a macro of itself (oh dear; I'm rather getting ahead of myself, it seems :P). -- Daniel
Sep 07 2007
prev sibling parent Reiner Pope <some address.com> writes:
Don Clugston wrote:
 With the q{ } token strings we can now do string mixins and get syntax 
 highlighting back. It's ugly, though:
 
 mixin(foo(q{a+b*c}));
 
 If we just had something which behaved as
 
 #define func(X) mixin(foo(q{X}))
 
 for example, something like:
 
 mixin alias foo func;
 
 which would declare func as a 'mixin alias' of foo.
 
 (of course, there's the 'macro' keyword as well; 'abstract' is also 
 interesting).
 allowing us to write:
 
 func(a+b*c);
 
 which would be instantly applicable. Almost all of the ugliness would 
 disappear from my BLADE code, for example.
 By contrast, I still haven't seen any use cases for the macros as 
 described in the conference presentation!

I see you said in the "Will macros work with expressions?" thread that you assume/hope string mixins will also get hygiene constructs. That would be great, but there are problems with string mixins that I can't see how they can be solved, but which should work with macros. --- One common pattern [well, I've done it a few times ;-)] is to write a string mixin which calls what should ideally be a private function. For instance, I'm working on pattern matching based on "unapply" functions. There is a public interface to the pattern matching; this interface can be automatically generated based on a given "unapply" function, and it serves to generate the code for each specific pattern the user wants to match. Ideally, "unapply" should not be exposed, as it is an implementation detail. However, since the pattern matching can bind results to variables, it needs to be mixed into the user's code. Since the pattern matching works by calling "unapply", and this is mixed into the user's code, unapply must be made public. This is one of a number of problems which arise from the fact that overload and symbol resolution is always done at the place where the string is mixed in. Hijacking of symbols by namesake functions at the call-site can cause further problems. This can be resolved using FQNs everywhere, but .stringof needs to assist in this, which it currently doesn't. But even FQNs fail if the symbol you need comes from a module that hasn't been imported at the call site, or is invisible -- like my first example -- because of protection attributes. I would assume that, with macros, symbols not preceeded by $ are resolved at the macro's declaration site, whereas those with $ are resolved at the instantiation site. This would completely solve the problem I mentioned above, because the "unapply" function would be resolved at the right time and never hijacked. --- Debugging generated and mixed-in code is a pain, partly because line numbers are wrong (and partly because it's hard to write the generator to check for user errors). For instance, my pattern matching currently looks like the following: ulong nat2int(Nat n) { mixin(Match!(Nat, "n", Case!(Nat.Zero_!(), "return 0;"), Case!(Nat.Succ_!("x"), "return nat2int(x) + 1;") )); } The point of interest is that there is user code in quotes there which is passed into the big machine of CTFE and metaprogramming, and then reappears in the nat2int function when the stuff is mixed in. Now, if that code was kept in some AST form and not as a string, the compiler could associate line and column information with it. If there were an error in the user code: for instance, if x was bound with type 'string', so that nat2int(x) was a type error, then the compiler could then point to the occurrence of nat2int(x) in the original source code, making the error much easier to find. I don't know how to easily do this with string mixins and have it work even when the string is sliced and otherwise modified. One possibility would be to say that q{...} string literals have a #line 6 "file.d" inserted at the start. This could probably solve the debugging problem, but it is still not as clean as code manipulation. For instance, keeping it as an AST would allow IDEs to support "jump to definition", even within mixins, whereas the #line 6 "file.d" info doesn't actually convey the point that the code *actually came from #line 6 "file.d"*. -- Reiner
Sep 08 2007