digitalmars.D - Eponymous/anonymous mixin templates
- Jeffrey Tsang (39/39) Jun 07 2015 I've been doing some fun template metaprogramming recently for a
- Artur Skawina via Digitalmars-d (12/13) Jun 07 2015 AFAICT you're asking for the commented-out line in
- Jeffrey Tsang (22/36) Jun 08 2015 There are three separate things I would like to have:
- Artur Skawina via Digitalmars-d (27/74) Jun 08 2015 This part already works, if you leave out the 'mixin' annotation
- Jeffrey Tsang (17/92) Jun 08 2015 Recursive mixin templates, which is mostly the reason I'm using
- Jeffrey Tsang (4/107) Jun 08 2015 I meant
- Artur Skawina via Digitalmars-d (18/52) Jun 09 2015 No, it's just another D "feature" that gets in the way -
I've been doing some fun template metaprogramming recently for a few projects, and I have to say the expressive power of mixin templates is quite staggering. I've noticed a strongly recurring pattern in my usage (mostly trying to get compile-time templated code generation, "parser from template args for input spec" for example): A lot of the time, I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once. This has a strong connection with both the eponymous template trick and anonymous expressions, and essentially is a combination of both - a templated anon expression looked up at instantiation site. Currently, the standard workaround is defining a dummy name within the mixin template to do the work. The point of the eponymous trick is of course, to avoid this namespace pollution, or at least brainspace pollution. The easiest solution I can think of is to create a second type of MixinExpression: "mixin" "(" MixinTemplateName TemplateArguments_opt ")" which extends the existing string mixin syntax. This expression gets rewritten into the symbol declared as the MixinTemplateName, along the lines of the eponymous trick alias, which is instantiated as a mixin. The mixin template contents are otherwise not imported into scope. The only hypothetical ambiguity is if the mixin template itself resolves to a string literal, whether the result is to be compiled or not. However, it requires explicitly naming a mixin template, which is categorically different from a string, and to me it's completely safe to treat the result of the expression as a literal string symbol. With this, an extended request/alternate solution would be to allow writing eponymous mixin templates as well, in the same way normal eponymous templates are. What this looks to me is effectively making "mixin" a modifier keyword on templates in general, to bind at instantiation site rather than declaration site, and following all the logical consequences. Current TemplateMixins naturally become general instantiation imports, like using a normal template in a noop.
Jun 07 2015
On 06/07/15 11:05, Jeffrey Tsang via Digitalmars-d wrote:I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once.AFAICT you're asking for the commented-out line in auto Tmpl() = l; void main(string[] argv) { auto l = argv.length; mixin Tmpl!() a; assert(a.Tmpl==l); //assert(a==l); } to work. That would probably be enough, make sense and have no serious backward compat issues. artur
Jun 07 2015
On Sunday, 7 June 2015 at 14:17:45 UTC, Artur Skawina wrote:On 06/07/15 11:05, Jeffrey Tsang via Digitalmars-d wrote:There are three separate things I would like to have: 1. Eponymous trick mixin T foo(T)() { return bar; } as pure syntactic sugar for mixin template foo(T) { T foo() { return bar; } } 2. Eponymous trick, calling end mixin foo!T; to also include as syntactic support alias foo = foo!T.foo; as well as the named version you listed. 3. Inline/anonymous mixins Some way of writing the equivalent of bar = (mixin foo!T)() + 3; inline as an expression, the same way as bar = regular_template_foo!T() + 3;I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once.AFAICT you're asking for the commented-out line in auto Tmpl() = l; void main(string[] argv) { auto l = argv.length; mixin Tmpl!() a; assert(a.Tmpl==l); //assert(a==l); } to work. That would probably be enough, make sense and have no serious backward compat issues. artur
Jun 08 2015
On 06/08/15 17:14, Jeffrey Tsang via Digitalmars-d wrote:On Sunday, 7 June 2015 at 14:17:45 UTC, Artur Skawina wrote:This part already works, if you leave out the 'mixin' annotation (which only prevents non-mixin use). "Normal" templates can be mixed in too, see my `Tmpl` example above. T foo(T)() { return bar; } auto f(int bar) { mixin foo!double blah; return blah.foo(); } void main() { assert (f(42)==42.0); }On 06/07/15 11:05, Jeffrey Tsang via Digitalmars-d wrote:There are three separate things I would like to have: 1. Eponymous trick mixin T foo(T)() { return bar; } as pure syntactic sugar for mixin template foo(T) { T foo() { return bar; } }I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once.AFAICT you're asking for the commented-out line in auto Tmpl() = l; void main(string[] argv) { auto l = argv.length; mixin Tmpl!() a; assert(a.Tmpl==l); //assert(a==l); } to work. That would probably be enough, make sense and have no serious backward compat issues.2. Eponymous trick, calling end mixin foo!T; to also include as syntactic support alias foo = foo!T.foo; as well as the named version you listed.No, that would hide the `foo` template symbol. There's no such problem with the named version (other than the eponymous look-up not working).3. Inline/anonymous mixins Some way of writing the equivalent of bar = (mixin foo!T)() + 3;This part I'm not sure about. Can't think of an interesting use case, that wouldn't be better handled in some other way. The obvious workaround would be: bar = { mixin foo!T f; return f()+3; }(); // today: `f.foo()+3` [`{...}()` might result in a lambda/closure right now, but that should really be fixed (ie defined as a special case)] artur
Jun 08 2015
On Monday, 8 June 2015 at 16:27:36 UTC, Artur Skawina wrote:On 06/08/15 17:14, Jeffrey Tsang via Digitalmars-d wrote:Recursive mixin templates, which is mostly the reason I'm using this, won't work: string foo(string x)() { return x; } string foo(string x, T...)() { mixin foo!T _foo; return x ~ y ~ _foo.foo(); } // mixin foo!("a", "b"); // dies on foo not a templateOn Sunday, 7 June 2015 at 14:17:45 UTC, Artur Skawina wrote:This part already works, if you leave out the 'mixin' annotation (which only prevents non-mixin use). "Normal" templates can be mixed in too, see my `Tmpl` example above. T foo(T)() { return bar; } auto f(int bar) { mixin foo!double blah; return blah.foo(); } void main() { assert (f(42)==42.0); }On 06/07/15 11:05, Jeffrey Tsang via Digitalmars-d wrote:There are three separate things I would like to have: 1. Eponymous trick mixin T foo(T)() { return bar; } as pure syntactic sugar for mixin template foo(T) { T foo() { return bar; } }I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once.AFAICT you're asking for the commented-out line in auto Tmpl() = l; void main(string[] argv) { auto l = argv.length; mixin Tmpl!() a; assert(a.Tmpl==l); //assert(a==l); } to work. That would probably be enough, make sense and have no serious backward compat issues.I just mean the eponymous look-up part of it, whatever the compiler-generated alias looks like.2. Eponymous trick, calling end mixin foo!T; to also include as syntactic support alias foo = foo!T.foo; as well as the named version you listed.No, that would hide the `foo` template symbol. There's no such problem with the named version (other than the eponymous look-up not working).Yeah, it's probably easier to do auto foo = { mixin foo!T f; return f.foo; }() and take one name. With the mixin using enclosing scope, can it be function and not delegate?3. Inline/anonymous mixins Some way of writing the equivalent of bar = (mixin foo!T)() + 3;This part I'm not sure about. Can't think of an interesting use case, that wouldn't be better handled in some other way. The obvious workaround would be: bar = { mixin foo!T f; return f()+3; }(); // today: `f.foo()+3` [`{...}()` might result in a lambda/closure right now, but that should really be fixed (ie defined as a special case)] artur
Jun 08 2015
On Monday, 8 June 2015 at 20:04:00 UTC, Jeffrey Tsang wrote:On Monday, 8 June 2015 at 16:27:36 UTC, Artur Skawina wrote:I meant auto foo = { mixin foo!T f; return &f.foo; }(); and then use the function normally.On 06/08/15 17:14, Jeffrey Tsang via Digitalmars-d wrote:Recursive mixin templates, which is mostly the reason I'm using this, won't work: string foo(string x)() { return x; } string foo(string x, T...)() { mixin foo!T _foo; return x ~ y ~ _foo.foo(); } // mixin foo!("a", "b"); // dies on foo not a templateOn Sunday, 7 June 2015 at 14:17:45 UTC, Artur Skawina wrote:This part already works, if you leave out the 'mixin' annotation (which only prevents non-mixin use). "Normal" templates can be mixed in too, see my `Tmpl` example above. T foo(T)() { return bar; } auto f(int bar) { mixin foo!double blah; return blah.foo(); } void main() { assert (f(42)==42.0); }On 06/07/15 11:05, Jeffrey Tsang via Digitalmars-d wrote:There are three separate things I would like to have: 1. Eponymous trick mixin T foo(T)() { return bar; } as pure syntactic sugar for mixin template foo(T) { T foo() { return bar; } }I use a mixin template to define exactly one symbol, and at instantiation I wish to use that symbol immediately, once.AFAICT you're asking for the commented-out line in auto Tmpl() = l; void main(string[] argv) { auto l = argv.length; mixin Tmpl!() a; assert(a.Tmpl==l); //assert(a==l); } to work. That would probably be enough, make sense and have no serious backward compat issues.I just mean the eponymous look-up part of it, whatever the compiler-generated alias looks like.2. Eponymous trick, calling end mixin foo!T; to also include as syntactic support alias foo = foo!T.foo; as well as the named version you listed.No, that would hide the `foo` template symbol. There's no such problem with the named version (other than the eponymous look-up not working).Yeah, it's probably easier to do auto foo = { mixin foo!T f; return f.foo; }() and take one name. With the mixin using enclosing scope, can it be function and not delegate?3. Inline/anonymous mixins Some way of writing the equivalent of bar = (mixin foo!T)() + 3;This part I'm not sure about. Can't think of an interesting use case, that wouldn't be better handled in some other way. The obvious workaround would be: bar = { mixin foo!T f; return f()+3; }(); // today: `f.foo()+3` [`{...}()` might result in a lambda/closure right now, but that should really be fixed (ie defined as a special case)] artur
Jun 08 2015
On 06/08/15 22:03, Jeffrey Tsang via Digitalmars-d wrote:Recursive mixin templates, which is mostly the reason I'm using this, won't work: string foo(string x)() { return x; } string foo(string x, T...)() { mixin foo!T _foo; return x ~ y ~ _foo.foo(); } // mixin foo!("a", "b"); // dies on foo not a templateNo, it's just another D "feature" that gets in the way - the name of a template resolves to the current instantiation when used inside the template. If your templates are in module scope the workaround is simple: string foo(string x, T...)() { mixin .foo!T _foo; return x ~ _foo.foo(); } It they are not, it gets more interesting.3. Inline/anonymous mixins Some way of writing the equivalent of bar = (mixin foo!T)() + 3;This part I'm not sure about. Can't think of an interesting use case, that wouldn't be better handled in some other way. The obvious workaround would be: bar = { mixin foo!T f; return f()+3; }(); // today: `f.foo()+3` [`{...}()` might result in a lambda/closure right now, but that should really be fixed (ie defined as a special case)]Yeah, it's probably easier to do auto foo = { mixin foo!T f; return f.foo; }() and take one name. With the mixin using enclosing scope, can it be function and not delegate?No, but a delegate that's immediately executed could be placed inline and does not require a closure. Some way to use statements and declarations inside expressions is needed anyway, and this syntax would be natural in D (even C compilers have this as an extension, eg gcc's "statement expressions").I meant auto foo = { mixin foo!T f; return &f.foo; }(); and then use the function normally.That may work for function, which have effectively infinite lifetime, but is not a good idea for other objects. artur
Jun 09 2015