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