mixin macros -- so tantalizing

Reiner Pope some at address.com
Sat Sep 8 03:06:53 PDT 2007


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



More information about the Digitalmars-d mailing list