| Thread overview | |||||||||
|---|---|---|---|---|---|---|---|---|---|
|
June 07, 2015 Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
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.
| ||||
June 07, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jeffrey Tsang | 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
| |||
June 08, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Artur Skawina | 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:
>> 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
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;
| |||
June 08, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jeffrey Tsang | 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: >> 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. > > 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; > } > } 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); } > 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 | |||
June 08, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Artur Skawina | 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: >> 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: >>>> 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. >> >> 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; >> } >> } > > 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); > } > 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 template >> 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). > I just mean the eponymous look-up part of it, whatever the compiler-generated alias looks like. >> 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 > 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? | |||
June 08, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jeffrey Tsang | 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:
>> 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:
>>>> 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.
>>>
>>> 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;
>>> }
>>> }
>>
>> 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);
>> }
>>
>
> 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 template
>
>>> 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).
>>
> I just mean the eponymous look-up part of it, whatever the compiler-generated alias looks like.
>
>>> 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
>>
>
> 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?
I meant
auto foo = { mixin foo!T f; return &f.foo; }();
and then use the function normally.
| |||
June 09, 2015 Re: Eponymous/anonymous mixin templates | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jeffrey Tsang | 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 template No, 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 | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply