www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Mixin template parameters / mixin template literals

reply =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
Consider:

     sort!("a > b")(array);

This is almost perfect, except for "a > b" being a string instead 
of "real" code. If "a > b" was an actual block of code it could 
be syntax highlighted by the editor, directly grammar checked by 
compiler, etc. The only impediment to that is that a and b are 
declared in the sort() body scope, so "a > b" has to be mixed-in 
later in sort().

An alternative is using a lambda:

     sort!((a, b) => a > b)(array);

Now we have code which is directly parsed and highlighted, but it 
is more verbose (is there any other difference? I suppose there's 
no performance penalty, because the templated sort() creates a 
function from the string "a > b" anyway).

It would seem that the best of both worlds would be for sort() to 
specify that the "less" type parameter could be actual code, but 
that it should be mixed-in inside sort(). Well, since we have 
lazy parameters, I was wondering why couldn't we have mixin 
parameters. So I tried this, which  works:

     // create our "a > b" as code (not string) that is mixed-in 
later
     mixin template Greater() {
         auto Greater = () => a > b;
     }

     // example of a sort which supports template mixins
     void foo_sort(alias G = Greater, Range)(Range r)
     {
         bool funG(Range)(ElementType!(Range) a, 
ElementType!(Range) b)
         {
             mixin G;
             return Greater(); // Greater() because G() doesn't 
work (unhelpful alias, see previous thread)
         }
         sort!(funG!(Range))(r);
     }

     void main()
     {
         int[] array = [3, 1, 5, 2, 7];
         foo_sort!(Greater)(array);
         writefln("%s", array);
     }

So, the infrastructure seems to be there, we just had to declare 
Greater() outside the sort() call site because we have neither 
mixin template parameters nor mixin template literals. If we had 
one of those we could do either:

     void foo_sort(mixin G, Range)(Range r) { ... } // or mixin 
template G
     foo_sort!(a > b)(array);

or

     foo_sort!({a > b})(array); // some kind of mixin template 
literal

Of course, this isn't anything specific to template functions and 
sort. This is a kind of macro, just like the lazy type specifier, 
so I it would be useful in other places.

OK, so this is probably a bit crazy, but I had to share these 
thoughts. Feel free to proceed to demolish all of this ;-)
Apr 23 2013
next sibling parent reply "Diggory" <diggsey googlemail.com> writes:
One way this could be done is by expanding on lazy parameters. 
Perhaps you could specify parameters for a lazy argument, so 
instead of always getting a delegate of type T delegate(), you 
could get one of type T delegate(U a, V b).

For example, a parameter could be:
void f(lazy bool exp(int a, int b)) {
     bool result = exp(1, 2);
}

Then when you call that:
f(a < b)

The names 'a' and 'b' come from the parameter list for 'f'. I 
would definitely vote for this as a feature!
Apr 24 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04/24/2013 07:07 PM, Diggory wrote:
 One way this could be done is by expanding on lazy parameters. Perhaps
 you could specify parameters for a lazy argument, so instead of always
 getting a delegate of type T delegate(), you could get one of type T
 delegate(U a, V b).

 For example, a parameter could be:
 void f(lazy bool exp(int a, int b)) {
      bool result = exp(1, 2);
 }

 Then when you call that:
 f(a < b)

 The names 'a' and 'b' come from the parameter list for 'f'. I would
 definitely vote for this as a feature!

This is a little dangerous as it looks like an ordinary expression but has to somehow affect symbol scoping.
Apr 24 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/24/2013 11:03 PM, Diggory wrote:
 On Wednesday, 24 April 2013 at 20:32:16 UTC, Timon Gehr wrote:
 On 04/24/2013 07:07 PM, Diggory wrote:
 One way this could be done is by expanding on lazy parameters. Perhaps
 you could specify parameters for a lazy argument, so instead of always
 getting a delegate of type T delegate(), you could get one of type T
 delegate(U a, V b).

 For example, a parameter could be:
 void f(lazy bool exp(int a, int b)) {
     bool result = exp(1, 2);
 }

 Then when you call that:
 f(a < b)

 The names 'a' and 'b' come from the parameter list for 'f'. I would
 definitely vote for this as a feature!

This is a little dangerous as it looks like an ordinary expression but has to somehow affect symbol scoping.

Surely it's no more dangerous than lazy parameters already are. (they are already converted into a hidden delegate, it just happens to take no parameters at the moment)

It surely is. 'lazy' parameters do not affect symbol lookup in the passed expressions.
Apr 24 2013
prev sibling next sibling parent "Tove" <tove fransson.se> writes:
On Wednesday, 24 April 2013 at 02:18:07 UTC, Luís Marques wrote:
 Consider:

     sort!("a > b")(array);

how about? sort!(q{a > b})(array); http://dlang.org/lex.html#TokenString
Apr 24 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Tove:

 how about?
 sort!(q{a > b})(array);

Today we usually write it this way: array.sort!q{ a > b }; Bye, bearophile
Apr 24 2013
prev sibling next sibling parent =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
On Wednesday, 24 April 2013 at 20:50:31 UTC, bearophile wrote:
 Today we usually write it this way:
 array.sort!q{ a > b };

Thanks. (I just copied it from the docs)
Apr 24 2013
prev sibling next sibling parent "Diggory" <diggsey googlemail.com> writes:
On Wednesday, 24 April 2013 at 20:32:16 UTC, Timon Gehr wrote:
 On 04/24/2013 07:07 PM, Diggory wrote:
 One way this could be done is by expanding on lazy parameters. 
 Perhaps
 you could specify parameters for a lazy argument, so instead 
 of always
 getting a delegate of type T delegate(), you could get one of 
 type T
 delegate(U a, V b).

 For example, a parameter could be:
 void f(lazy bool exp(int a, int b)) {
     bool result = exp(1, 2);
 }

 Then when you call that:
 f(a < b)

 The names 'a' and 'b' come from the parameter list for 'f'. I 
 would
 definitely vote for this as a feature!

This is a little dangerous as it looks like an ordinary expression but has to somehow affect symbol scoping.

Surely it's no more dangerous than lazy parameters already are. (they are already converted into a hidden delegate, it just happens to take no parameters at the moment)
Apr 24 2013
prev sibling next sibling parent =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
On Wednesday, 24 April 2013 at 20:32:16 UTC, Timon Gehr wrote:
 Then when you call that:
 f(a < b)

expression but has to somehow affect symbol scoping.

That's true, and that was one reason I suggested some kind of template mixin literal. On the other hand, that's just an extreme version of what happens with lazy, where at the call site you can't tell that the argument side effects are not produced immediately. Without experience with features like this it's hard to tell if their usefulness pays for their cost, but it seems to me that something like this could make for very powerful macros. On Wednesday, 24 April 2013 at 17:38:34 UTC, Tove wrote:
 how about?
 sort!(q{a > b})(array);

 http://dlang.org/lex.html#TokenString

I was not aware of the token strings, is that new? Boy, I really should spend more time around here. That's an excellent suggestion, although I think these "mixin parameters" we are discussing are more like mixin template parameters than mixin string parameters.
Apr 24 2013
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
 Consider:

     sort!("a > b")(array);

 This is almost perfect, except for "a > b" being a string 
 instead of "real" code. If "a > b" was an actual block of code 
 it could be syntax highlighted by the editor, directly grammar 
 checked by compiler, etc.

Is changing the language the right approach to this or would smarter IDEs possibly be a better direction?
Apr 24 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 24 April 2013 at 17:38:34 UTC, Tove wrote:
 On Wednesday, 24 April 2013 at 02:18:07 UTC, Luís Marques wrote:
 Consider:

    sort!("a > b")(array);

how about? sort!(q{a > b})(array); http://dlang.org/lex.html#TokenString

Token strings solve all the problems the OP mentioned, but they do not solve the one problem he didn't mention - closures: int[] array = [3, 1, 5, 2, 7]; int x = 4; writeln(array.filter!(a => a < x)()); // works as expected and prints "[3, 1, 2]" writeln(array.filter!q{a < x}()); // Error: undefined identifier x
Apr 24 2013
prev sibling next sibling parent =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
On Wednesday, 24 April 2013 at 23:04:02 UTC, ixid wrote:
 Is changing the language the right approach to this or would 
 smarter IDEs possibly be a better direction?

A smarter IDE always helps :-) It might not be worth changing the language for this (or it might), but changing the language to have first-class support of constructs like this would allow tighter checking of the mixed-in code, which seems to me to be mainly the role of the compiler, and not of the IDE. For instance, by using token strings, as suggested by Tove, you can have a tighter grip on the mixed-in string, which should lead to smarter error messages (gramatical check), but not as good as first-class support (semantic checks, etc). But I don't think this is only about error checking, it's about creating a better abstraction: I think template mixin are a less brittle abstraction than string mixins, but without the argument type modifiers they can't replace the string mixins. But don't take my opinion too seriously, I'm sure you guys know the language better than I do :-)
Apr 24 2013
prev sibling next sibling parent =?UTF-8?B?Ikx1w61z?= Marques" <luismarques gmail.com> writes:
On Wednesday, 24 April 2013 at 23:42:57 UTC, Idan Arye wrote:
 Token strings solve all the problems the OP mentioned, but they 
 do not solve the one problem he didn't mention - closures:

     int[] array = [3, 1, 5, 2, 7];
     int x = 4;
     writeln(array.filter!(a => a < x)()); // works as expected 
 and prints "[3, 1, 2]"
     writeln(array.filter!q{a < x}()); // Error: undefined 
 identifier x

Yeah, it was unfortunate that the problems I exemplified could be solved by the token strings, without fulling solving the problem, making the issue seem more superficial than it is. Your example is better. Notice that my code worked for your example, if you changed it to match your scenario: // we have to move this outside of main, // because the mixin template cannot be there int[] array; int x = 4; mixin template Greater() { auto Greater = () => a < x; } (...) This seems to work. So, as I said, the infrastructure is there, we are just lacking the convenience of template mixin literals. Without that we have this unwieldy code, which otherwise seems fine.
Apr 24 2013
prev sibling next sibling parent "renoX" <renozyx gmail.com> writes:
Are you really saying that 'sort!((a, b) => a > b)(array);' is 
too verbose??

I consider 'sort!("a > b")(array);' to be **too terse**: there is 
nothing here telling the reader what a and b are supposed to be.

BR,
renoX
Apr 26 2013
prev sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Friday, 26 April 2013 at 08:13:48 UTC, renoX wrote:
 I consider 'sort!("a > b")(array);' to be **too terse**: there 
 is nothing here telling the reader what a and b are supposed to 
 be.

Sure there is - it's called "convention". Since all the functions in `std.algorithm` that accept delegates also accept string literals for a mixin that use `a` and `b` as the arguments, the reader should know what `a` and `b` means, just like they know what `sort` means.
Apr 26 2013