Thread overview
Referring to alias parameters in a mixin template
Dec 17, 2014
aldanor
Dec 17, 2014
aldanor
Dec 17, 2014
anonymous
Dec 17, 2014
aldanor
Dec 17, 2014
anonymous
Dec 17, 2014
aldanor
Dec 17, 2014
ketmar
Dec 17, 2014
anonymous
Dec 17, 2014
aldanor
Dec 17, 2014
anonymous
December 17, 2014
Would something like this be possible at all? A hypothetical mixin template

    mixin template makeProperty(T, string name, alias func) {
        ...
    }

that could be called like this:

    makeProperty!(int, "foo", f)

and would generate code like

    int @property foo() { return f(); }

This is a very simplified example of what I'm trying to do, but I'm a bit stuck at this point -- if I'm generating the code as a string, how do I know how to refer to "func" alias (traits identifier / fullyQualifiedName just don't cut it for a lot of cases)? For one, thing, it could be an anonymous delegate like { return 0; } or symbol from another module or anything else. This is obviously doable if "func" is a string that gets mixed in, but what if it is an alias?

Without the "name" part, one could sure use a simple template:

    template makeUnnamedProperty(T, alias func) {
        T makeUnnamedProperty() @property { return func(); }
    }

and then this works...

    alias foo = makeUnnamedProperty!(int, f);

However, how does the one go about templating this  when "foo" is a (compile-time) string?

Wonder if I'm missing something...

Thanks.
December 17, 2014
A partial solution would be something like this:

    mixin template makeProperty(T, string name, alias func) {
        enum p = makeUnnamedProperty!(T, func);
        mixin("enum %s = p;".format(name)); // or alias
    }

however now the parent namespace is polluted with "p", is there any way to hide it away and/or avoid it?

I may be wrong, but I guess the whole thing boils down to a question whether it's possible to have a mixin template with signature 'bind(string name, alias symbol)' which generates 'enum foo = bar;' when called as 'mixin bind!("foo", bar)'?
December 17, 2014
On Wednesday, 17 December 2014 at 01:14:36 UTC, aldanor wrote:
> A partial solution would be something like this:
>
>     mixin template makeProperty(T, string name, alias func) {
>         enum p = makeUnnamedProperty!(T, func);
>         mixin("enum %s = p;".format(name)); // or alias
>     }
>
> however now the parent namespace is polluted with "p", is there any way to hide it away and/or avoid it?

The polution isn't too bad. Mixed-in symbols are second class. A
symbol not from a mixin would win, and multiple mixed-in `p`s
would only conflict on use.

But if you want to avoid `p`, just do the substitution:

     mixin template makeProperty(T, string name, alias func) {
         mixin("enum %s = makeUnnamedProperty!(T,
func);".format(name)); // or alias
     }
December 17, 2014
On Wednesday, 17 December 2014 at 01:39:07 UTC, anonymous wrote:
> But if you want to avoid `p`, just do the substitution:
>
>      mixin template makeProperty(T, string name, alias func) {
>          mixin("enum %s = makeUnnamedProperty!(T,
> func);".format(name)); // or alias
>      }

Thanks, that looks exactly like what I need -- I figured something like this would compile, but I guess it's slightly counterintuitive that you can access "T" and 'func" this way.

Wonder if this is doable within a single mixin template without using makeUnnamedProperty?
December 17, 2014
On Wednesday, 17 December 2014 at 01:49:14 UTC, aldanor wrote:
> Wonder if this is doable within a single mixin template without using makeUnnamedProperty?

Sure, straight forward:

      mixin template makeProperty(T, string name, alias func) {
          mixin("T %s() @property { return func();
}".format(name));
      }
December 17, 2014
On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
> Sure, straight forward:
>
>       mixin template makeProperty(T, string name, alias func) {
>           mixin("T %s() @property { return func();
> }".format(name));
>       }

Indeed... thanks! Just one thing that I find confusing here -- how
exactly do T and func resolve when this template is mixed in?
What if there was another local symbol named "func" in the target
scope?
December 17, 2014
On Wed, 17 Dec 2014 02:34:15 +0000
aldanor via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com>
wrote:

> On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
> > Sure, straight forward:
> >
> >       mixin template makeProperty(T, string name, alias func) {
> >           mixin("T %s() @property { return func();
> > }".format(name));
> >       }
> 
> Indeed... thanks! Just one thing that I find confusing here -- how exactly do T and func resolve when this template is mixed in? What if there was another local symbol named "func" in the target scope?
this is mixed inside the `makeProperty` template itself, not where you instantiated it. i.e. when compiler sees mixin, it not postponing it. so what you actually got is `makeProperty` template with `%s` substituted with `name`, and only then `makeProperty` is mixed at the place of it's instatiation.


December 17, 2014
On Wednesday, 17 December 2014 at 02:34:16 UTC, aldanor wrote:
> On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
>> Sure, straight forward:
>>
>>      mixin template makeProperty(T, string name, alias func) {
>>          mixin("T %s() @property { return func();
>> }".format(name));
>>      }
>
> Indeed... thanks! Just one thing that I find confusing here -- how
> exactly do T and func resolve when this template is mixed in?
> What if there was another local symbol named "func" in the target
> scope?

As far as I understand, the string mixin is resolved first, and
then the template mixin takes place. So the progression is
somewhat like this (pseudo code):

----
mixin makeProperty!(int, "foo", f);

/* Replace "makeProperty" with its definition. */
mixin (T, name, func){mixin("T %s() @property { return
func();}".format(name));}!(int, "foo", f);

/* First round, substitute arguments for parameters. `T` and
`func` are not replaced, because they're just string contents at
this point. */
mixin (T, name, func){mixin("T %s() @property { return
func();}".format("foo"));}!(int, "foo", f);

/* Evaluate `format` and do the string mixin. */
mixin (T, name, func){T foo() @property { return func();}}!(int,
"foo", f);

/* Second round, substitute arguments for parameters. This time,
`T` and `func` are replaced. */
mixin (T, name, func){int foo() @property { return f();}}!(int,
"foo", f);

/* Didn't do any string mixins in the second round, so there's no
need for a third round. Get rid of template parameters and
arguments. */
mixin {int foo() @property { return f();}};

/* Finally, do the template mixin. */
int foo() @property { return f();}
----

Not sure if that helps or maybe it just adds to the confusion.

As to if there were `T` or `func` in the target scope, they'd be
shadowed by the template parameters, no matter if there's a
string mixin or not.
December 17, 2014
On Wednesday, 17 December 2014 at 12:49:10 UTC, anonymous wrote:
> As far as I understand, the string mixin is resolved first, and
> then the template mixin takes place. So the progression is
> somewhat like this (pseudo code):
>
> ----
> mixin makeProperty!(int, "foo", f);
>
> /* Replace "makeProperty" with its definition. */
> mixin (T, name, func){mixin("T %s() @property { return
> func();}".format(name));}!(int, "foo", f);
>
> /* First round, substitute arguments for parameters. `T` and
> `func` are not replaced, because they're just string contents at
> this point. */
> mixin (T, name, func){mixin("T %s() @property { return
> func();}".format("foo"));}!(int, "foo", f);
>
> /* Evaluate `format` and do the string mixin. */
> mixin (T, name, func){T foo() @property { return func();}}!(int,
> "foo", f);
>
> /* Second round, substitute arguments for parameters. This time,
> `T` and `func` are replaced. */
> mixin (T, name, func){int foo() @property { return f();}}!(int,
> "foo", f);
>
> /* Didn't do any string mixins in the second round, so there's no
> need for a third round. Get rid of template parameters and
> arguments. */
> mixin {int foo() @property { return f();}};
>
> /* Finally, do the template mixin. */
> int foo() @property { return f();}
> ----
>
> Not sure if that helps or maybe it just adds to the confusion.
>
> As to if there were `T` or `func` in the target scope, they'd be
> shadowed by the template parameters, no matter if there's a
> string mixin or not.
That makes sense. So if I understand correctly, basically after each string mixin it has to check if any new symbols were leaked into the current scope and then try to resolve them, if any, with template parameters taking precedence over local variables? (This obviously has to repeat in case of nested mixins)

Thanks again!
December 17, 2014
On Wednesday, 17 December 2014 at 15:40:25 UTC, aldanor wrote:
> On Wednesday, 17 December 2014 at 12:49:10 UTC, anonymous wrote:
[...]
>> As to if there were `T` or `func` in the target scope, they'd be
>> shadowed by the template parameters, no matter if there's a
>> string mixin or not.
> That makes sense. So if I understand correctly, basically after each string mixin it has to check if any new symbols were leaked into the current scope and then try to resolve them, if any,

Well, every mixed-in string necessarily goes through a full
compiler pass. I don't think there's anything special going on
with regards to new symbols.

> with template parameters taking precedence over local variables? (This obviously has to repeat in case of nested mixins)

My wording was bad. There's no shadowing. When a template is
mixed in, the substitution of parameters happens before the
injection. That means, when the parameters are substituted there
are no local symbols yet. And when the code is injected there are
no parameters any more. So local symbols and parameters are never
considered at the same time. Obviously, parameters take
precedence, simply because they're considered earlier. But
there's no shadowing.

Disclaimer: I'm not a compiler dev. I don't know if/how all this
is specified (or I'd quote/link it). I'm just describing current
compiler behaviour and how it makes sense to me.