www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Variadic grouping

reply "JS" <js.mdnq gmail.com> writes:
Sometimes it's nice to be able to have groups of variadic 
parameters:

template t(T1..., T2...)

...

t!(a, b, c; d, e, f);

so that a,b,c are for T1 and d,e,f are for T2.

This can be done by making a symbol and breaking up a single 
variadic but is messy.

I doubt such a feature will ever get added but who knows...
Jul 29 2013
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
JS:

 I doubt such a feature will ever get added but who knows...

It seems a cute idea, but why don't you show two or more very different use cases? (Asking for a feature without showing use cases is not so good.) Bye, bearophile
Jul 29 2013
next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 07/29/13 17:03, bearophile wrote:
 errors += global.endGagging(oldGaggedErrors);
 
 It's replaced with:
 if (global.endGagging(oldGaggedErrors))
     errors = true;
 
 Looking at that code it's easy to think about code like this, that is not
currently supported:
 
 errors ||= global.endGagging(oldGaggedErrors);
 
 Is the "||=" operator useful?

Not really. errors |=!! global.endGagging(oldGaggedErrors); artur
Jul 29 2013
prev sibling next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 07/29/13 17:24, bearophile wrote:
 Maybe there is a way to allow that too:
 
 
 template Select(bool condition, T...) if (T.length == 2) {
     static if (condition)
         enum Select = T[0];
     else
         enum Select = T[1];
 }
 
 void main() {
     enum x = Select!(true, 10, 20);// error
     static assert(x == 10);
     int a = 1;
     int b = 2;
     alias y = Select!(true, a, b);
     assert(y == 1);
     alias T = Select!(true, int, long);
     static assert(is(T == int));
 }
 
 
 How do you tell apart values from not values? :-)

template Select(bool condition, T...) if (T.length == 2) { static if (__traits(compiles, function { alias _1 = T[0]; alias _2 = T[1]; })) alias Select = T[!condition]; else enum Select = T[!condition]; } artur
Jul 29 2013
prev sibling next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 07/29/13 22:43, Artur Skawina wrote:
 On 07/29/13 17:24, bearophile wrote:
 Maybe there is a way to allow that too:


 template Select(bool condition, T...) if (T.length == 2) {
     static if (condition)
         enum Select = T[0];
     else
         enum Select = T[1];
 }

 void main() {
     enum x = Select!(true, 10, 20);// error
     static assert(x == 10);
     int a = 1;
     int b = 2;
     alias y = Select!(true, a, b);
     assert(y == 1);
     alias T = Select!(true, int, long);
     static assert(is(T == int));
 }


 How do you tell apart values from not values? :-)

template Select(bool condition, T...) if (T.length == 2) { static if (__traits(compiles, function { alias _1 = T[0]; alias _2 = T[1]; })) alias Select = T[!condition]; else enum Select = T[!condition]; }

Or even: template Select(bool condition, T...) if (T.length == 2) { static if (__traits(compiles, function { alias _ = T[!condition]; })) alias Select = T[!condition]; else enum Select = T[!condition]; } but that's a bit /too much/ magic -- allowing alias y1 = Select!(true, b, int); assert(y1 == 2); alias y2 = Select!(false, b, int); assert(is(y2==int)); is "neat", but could be confusing... artur
Jul 29 2013
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/30/13 12:36 AM, Dicebot wrote:
 On Monday, 29 July 2013 at 14:53:10 UTC, monarch_dodra wrote:
 A simple "Group" struct will solve the problem without a second thought:

 struct Group(Args...)
 {
 alias Ungroup = Args;
 }

That is actually a TypeTuple that does not auto-expand. May be even worth defining it as "alias Ungroup = TypeTuple!(Args)" to be more self-documenting at cost of some redundancy. Also it does not need to be a struct, templates are not mandatory eponymous. I think it is best solution for topic starter problem in terms of ROI and worth inclusion into Phobos.

Agreed. Andrei
Jul 30 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Monday, 29 July 2013 at 13:30:58 UTC, bearophile wrote:
 JS:

 I doubt such a feature will ever get added but who knows...

It seems a cute idea, but why don't you show two or more very different use cases? (Asking for a feature without showing use cases is not so good.) Bye, bearophile

The usefulness should be obvious and I seriously doubt if someone thinks it is not then any example I could give would convince them otherwise. It came up for me trying to write a ternary if to use. struct tVariadicSplit { } template tuple(args...) { alias tuple = args; } template tMin(alias a, alias b) { static if (a < b) alias tMin = a; else alias tMin = b; } template tIf(alias cond, args...) { enum sp = std.typetuple.staticIndexOf!(tVariadicSplit, args); static if (sp < 0) enum spp = args.length; else enum spp = sp; static if (cond) alias tIf = args[0..tMin!($, spp)]; else alias tIf = args[tMin!($,spp+1)..$]; } I have to use tVariadicSplit to split the grouping(sure I could reduce the symbol name size, which I have done). Being able to write this as template tIf(alias cond, tArgs..., fArgs...) { static if (cond) alias tIf = tArgs; else alias tIf = fArgs; } Would be much much more elegant.
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
JS:

 The usefulness should be obvious and I seriously doubt if 
 someone thinks it is not then any example I could give would 
 convince them otherwise.

It's not obvious for me :-) Explaining the "obvious" is sometimes necessary.
 It came up for me trying to write a ternary if to use.


 struct tVariadicSplit { }
 template tuple(args...) { alias tuple = args; }
 template tMin(alias a, alias b)
 {
 	static if (a < b) alias tMin = a; else alias tMin = b;
 }

 template tIf(alias cond, args...)
 {
 	enum sp = std.typetuple.staticIndexOf!(tVariadicSplit, args);
 	static if (sp < 0) enum spp = args.length; else enum spp = sp;
     static if (cond) alias tIf = args[0..tMin!($, spp)];	else 
 alias tIf = args[tMin!($,spp+1)..$];
 }

(In your code I suggest to put a newline after each semicolon). This is one use case, to implement a static ternary operator with multiple arguments. (But having multiple arguments is not so common). A possible static ternary operator syntax: enum foo = ct_cond !? Foo!5 : Bar!6; But in my opinion the need for it is not strong enough, better to keep the language simpler. Do you have a second use case? Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
 A possible static ternary operator syntax:

 enum foo = ct_cond !? Foo!5 : Bar!6;

 But in my opinion the need for it is not strong enough, better 
 to keep the language simpler.

A static ternary operator is sometimes handy, this is working code: import std.typetuple: TypeTuple; template Iota(int n) { static if (n <= 0) alias TypeTuple!() Iota; else alias TypeTuple!(Iota!(n-1), n-1) Iota; } void main() { int[3] a, b; foreach (i; Iota!3) a[i] = b[i]; } With the recently introduced syntax for enum and templates you could assume this works: enum Iota(int n) = (n <= 0) ? TypeTuple!() : TypeTuple!(Iota!(n-1), n-1); But that's a regular ternary operator, so despite only one branch is computed, both are verified for type, because they have to return the same type, so it gives: Error: template instance test.Iota!-497 recursive expansion A static ternary operator is allowed to return two different types, so no type is computed for the other branch, and this works: enum Iota(int n) = (n <= 0) !? TypeTuple!() : TypeTuple!(Iota!(n-1), n-1); Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Monday, 29 July 2013 at 13:59:54 UTC, bearophile wrote:
 JS:

 The usefulness should be obvious and I seriously doubt if 
 someone thinks it is not then any example I could give would 
 convince them otherwise.

It's not obvious for me :-) Explaining the "obvious" is sometimes necessary.
 It came up for me trying to write a ternary if to use.


 struct tVariadicSplit { }
 template tuple(args...) { alias tuple = args; }
 template tMin(alias a, alias b)
 {
 	static if (a < b) alias tMin = a; else alias tMin = b;
 }

 template tIf(alias cond, args...)
 {
 	enum sp = std.typetuple.staticIndexOf!(tVariadicSplit, args);
 	static if (sp < 0) enum spp = args.length; else enum spp = sp;
    static if (cond) alias tIf = args[0..tMin!($, spp)];	else 
 alias tIf = args[tMin!($,spp+1)..$];
 }

(In your code I suggest to put a newline after each semicolon). This is one use case, to implement a static ternary operator with multiple arguments. (But having multiple arguments is not so common). A possible static ternary operator syntax: enum foo = ct_cond !? Foo!5 : Bar!6;

I suggested that a while back.
 But in my opinion the need for it is not strong enough, better 
 to keep the language simpler.

Possibly because it doesn't exist? The problem is that when working with recursion on variadic parameters you have to deal with code like t!(args[0], args[1..$]) Sometimes you only want to return the last half of the args IF something is true, e.g., t!(args[0], tIf!(cond, args[1..$-2] else ";")); or whatever. The problem being that args is a tuple and you can't distinguish between what you want to return as true and what you want to return as false. To get around this you either have to create a split manually OR write static if's that create a lot of redundant code.
 Do you have a second use case?

Then a third? The fact whether there is a use case or not, or whether I have one or not should be irrelevant. The issue should stand on it's own. Having some easily way to use multiple variadic parameters is either useful or not. If it is and not hard to implement then it should be implemented. People can't use something if it doesn't exist... and will find ways around the real problem. I think with a little work you could come up with many more and better use cases than I could. They all will related to a sort of this or that scenario though because else why would one need to split up the arguments in the first place if there was no need to distinguish the group. Join!(Strings...; Delims...) could Join the strings with their Delims. Map!(Objects...; Funcs...) Could be an easy way to apply functions to objects. most of these could be done by using a pair of values though but would be easier to use multiple variadics. Split!(Strings...; Splits...) could split each strings with all the split possibilities. e.g. Split!("abcdefg", "abc"; "b", "c") will split each string using each split. returns (["a", "defg"], ["a"]) Anyways, If you don't build it they won't come...
Jul 29 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 29 July 2013 at 14:21:45 UTC, JS wrote:
 Then a third? The fact whether there is a use case or not, or 
 whether I have one or not should be irrelevant. The issue 
 should stand on it's own. Having some easily way to use 
 multiple variadic parameters is either useful or not.

There are a mind-boggling large number of things that are useful, but that's not the criteria for adding something to a programming language. A new feature has to be *sufficiently* useful to justify the increased complexity of the language and it's implementations (plus not clashing badly with other features). Example use cases are a useful way of demonstrating just how useful a feature can be.
Jul 29 2013
prev sibling next sibling parent "Robert Clipsham" <robert octarineparrot.com> writes:
On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

You can achieve this like so: ---- template Outer(T...) { template Inner(U...) { // Do something with T and U } } Outer!(a, b, c).Inner!(d, e, f); ---- You can see an example of it in action here (type tuple intersection): https://github.com/mrmonday/misc/blob/master/misc/misc.d#L5 Robert
Jul 29 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 29 July 2013 at 14:34:13 UTC, John Colvin wrote:
 On Monday, 29 July 2013 at 14:21:45 UTC, JS wrote:
 Then a third? The fact whether there is a use case or not, or 
 whether I have one or not should be irrelevant. The issue 
 should stand on it's own. Having some easily way to use 
 multiple variadic parameters is either useful or not.

There are a mind-boggling large number of things that are useful, but that's not the criteria for adding something to a programming language. A new feature has to be *sufficiently* useful to justify the increased complexity of the language and it's implementations (plus not clashing badly with other features). Example use cases are a useful way of demonstrating just how useful a feature can be.

The need for "grouping variadics" creeps up every now and then, and it *would* be useful to provide support for it. It may seem like there isn't really a need, but it's not like a lot of people do meta programming with recursive variadics. But for those that *do* the requirement is obvious. That said, it is *far* from requiring a language change. A simple "Group" struct will solve the problem without a second thought: struct Group(Args...) { alias Ungroup = Args; } So, if we take into account that phobos already has a static if for types (called select), it can become: //---- void main() { enum a = false; alias K = Select!(a, Group!(int, 1), Group!(double, 2)).Ungroup; } //---- See? There's no need to make such a fuss about this. "Group", contrary to Tuple, is not meant to ever be instanciated. This makes it better in this scenario, as "Tuple!5" will not compile (how do you construct an instance of type "5")? Another advantage is that "Group" is an explicit type, which means you can for a template that doesn't have "alias" parameters to accpet values. EG: Select!(cond, 1, 2);// This doesn't compile Select!(a, Group!(1), Group!(2)).Ungroup; //But this does :) It's a bit hackish, but it saves your butt. The "issue" (I think) is that we don't have "Group" in phobos, forcing our users (eg. JS), to re-invent and re-write the wheel, over and over again, which is not great. Placing this right under TypeTuple in std.typetuple seems ideal? Should I submit a pull/ER ?
Jul 29 2013
prev sibling next sibling parent "Robert Clipsham" <robert octarineparrot.com> writes:
On Monday, 29 July 2013 at 14:46:02 UTC, Robert Clipsham wrote:
 You can achieve this like so:
 ----
 template Outer(T...) {
     template Inner(U...) {
         // Do something with T and U
     }
 }
 Outer!(a, b, c).Inner!(d, e, f);
 ----

 You can see an example of it in action here (type tuple 
 intersection):
 https://github.com/mrmonday/misc/blob/master/misc/misc.d#L5

 Robert

What would be more interesting would be to have the ability to have variadic variadics, so you could use something like: ---- template Outer(T...) { template Inner(U... ...) { // Do something with T, U[0], U[1], U[2..$] } } Outer!(a, b, c).Inner!(d, e, f).Inner!(g, h, i); // Etc ---- Of course that raises the question of variadic variadic variadics and variadic variadic variadic variadics and so on, and my proposed syntax is already silly............
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
John Colvin:

 There are a mind-boggling large number of things that are 
 useful, but that's not the criteria for adding something to a 
 programming language.

 A new feature has to be *sufficiently* useful to justify the 
 increased complexity of the language and it's implementations 
 (plus not clashing badly with other features). Example use 
 cases are a useful way of demonstrating just how useful a 
 feature can be.

As example take a look at this commit, a small part of the changes to port the D front-end to D: https://github.com/D-Programming-Language/dmd/compare/428e559e5f0b...68ab05dba14a In this original line 'errors' is a boolean: errors += global.endGagging(oldGaggedErrors); It's replaced with: if (global.endGagging(oldGaggedErrors)) errors = true; Looking at that code it's easy to think about code like this, that is not currently supported: errors ||= global.endGagging(oldGaggedErrors); Is the "||=" operator useful? Is it working well with the rest of the language? Is it easy to understand a read? It is not bug-prone given the existence of the "|=" operator? Even if all those answers are positive, you have to ask yourself if it's also sufficiently useful... Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
monarch_dodra:

 Select!(cond, 1, 2);// This doesn't compile

Time ago I asked for an improvement of Select: https://github.com/D-Programming-Language/phobos/pull/1235 Maybe there is a way to allow that too: template Select(bool condition, T...) if (T.length == 2) { static if (condition) enum Select = T[0]; else enum Select = T[1]; } void main() { enum x = Select!(true, 10, 20);// error static assert(x == 10); int a = 1; int b = 2; alias y = Select!(true, a, b); assert(y == 1); alias T = Select!(true, int, long); static assert(is(T == int)); } How do you tell apart values from not values? :-) Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
 How do you tell apart values from not values? :-)

Is this brutal enough? :-) enum SameKind(T...) = __traits(compiles, {enum x = T[0], y = T[1]; }) || __traits(compiles, {alias x = T[0]; alias y = T[1]; }); template Select(bool condition, T...) if (T.length == 2 && SameKind!T) { static if (__traits(compiles, {enum x = T[0];})) { static if (condition) enum Select = T[0]; else enum Select = T[1]; } else { static if (condition) alias Select = T[0]; else alias Select = T[1]; } } void main() { enum x = Select!(true, 10, 20); static assert(x == 10); int a = 1; int b = 2; alias y = Select!(true, a, b); assert(y == 1); alias T = Select!(true, int, long); static assert(is(T == int)); } Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
 template Select(bool condition, T...)
 if (T.length == 2 && SameKind!T) {
     static if (__traits(compiles, {enum x = T[0];})) {
         static if (condition)
             enum Select = T[0];
         else
             enum Select = T[1];
     } else {
         static if (condition)
             alias Select = T[0];
         else
             alias Select = T[1];
     }
 }

It works in simple cases and maybe it's worth changing in some way the Select of Phobos, to support values too. But to solve the problem in general I think there's a need for "lazy template arguments", similar to lazy function arguments, but for types. template Select(condition, lazy T1, lazy T2) {...} Bye, bearophile
Jul 29 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 29 July 2013 at 15:31:39 UTC, bearophile wrote:
 How do you tell apart values from not values? :-)

Is this brutal enough? :-) enum SameKind(T...) = __traits(compiles, {enum x = T[0], y = T[1]; }) || __traits(compiles, {alias x = T[0]; alias y = T[1]; }); template Select(bool condition, T...) if (T.length == 2 && SameKind!T) { static if (__traits(compiles, {enum x = T[0];})) { static if (condition) enum Select = T[0]; else enum Select = T[1]; } else { static if (condition) alias Select = T[0]; else alias Select = T[1]; } } void main() { enum x = Select!(true, 10, 20); static assert(x == 10); int a = 1; int b = 2; alias y = Select!(true, a, b); assert(y == 1); alias T = Select!(true, int, long); static assert(is(T == int)); } Bye, bearophile

But it's still "Select!(true, a, b)" and not "Select!(true, 1, 2)"... Simple overloads could help here? template Select(bool condition, T1, T2) { static if (condition) alias Select = T1; else alias Select = T2; } template Select(bool condition, alias T1, alias T2) { static if (condition) alias Select = T1; else alias Select = T2; } template Select(bool condition, alias T1, T2) { static if (condition) alias Select = T1; else alias Select = T2; } template Select(bool condition, T1, alias T2) { static if (condition) alias Select = T1; else alias Select = T2; } *Now* Select!(condition, 1, 2) works.
Jul 29 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 29 July 2013 at 15:57:36 UTC, monarch_dodra wrote:
 On Monday, 29 July 2013 at 15:31:39 UTC, bearophile wrote:
 How do you tell apart values from not values? :-)

Is this brutal enough? :-) enum SameKind(T...) = __traits(compiles, {enum x = T[0], y = T[1]; }) || __traits(compiles, {alias x = T[0]; alias y = T[1]; }); template Select(bool condition, T...) if (T.length == 2 && SameKind!T) { static if (__traits(compiles, {enum x = T[0];})) { static if (condition) enum Select = T[0]; else enum Select = T[1]; } else { static if (condition) alias Select = T[0]; else alias Select = T[1]; } } void main() { enum x = Select!(true, 10, 20); static assert(x == 10); int a = 1; int b = 2; alias y = Select!(true, a, b); assert(y == 1); alias T = Select!(true, int, long); static assert(is(T == int)); } Bye, bearophile

But it's still "Select!(true, a, b)" and not "Select!(true, 1, 2)"...

Wait never mind my above post. Your approach works.
Jul 29 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 29 July 2013 at 16:00:47 UTC, monarch_dodra wrote:
 Wait never mind my above post. Your approach works.

Except for this... alias K = Select!(true, 4, int); ;) Useful? I don't think so... but I thought I'd point it out ^^
Jul 29 2013
prev sibling next sibling parent "Meta" <jared771 gmail.com> writes:
On Monday, 29 July 2013 at 16:02:02 UTC, monarch_dodra wrote:
 Except for this...
 alias K = Select!(true, 4, int);

 ;)

 Useful? I don't think so... but I thought I'd point it out ^^

I think it's a feature that this doesn't work. As for variadic grouping, named parameters could easily solve this problem, and have applications for other situations beside variadic arguments.
Jul 29 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Monday, 29 July 2013 at 16:52:17 UTC, Meta wrote:
 On Monday, 29 July 2013 at 16:02:02 UTC, monarch_dodra wrote:
 Except for this...
 alias K = Select!(true, 4, int);

 ;)

 Useful? I don't think so... but I thought I'd point it out ^^

I think it's a feature that this doesn't work. As for variadic grouping, named parameters could easily solve this problem, and have applications for other situations beside variadic arguments.

I'm not sure how named parameters would solve the original problem but using a syntax like what I'm suggesting one can do stuff like template Log(alias cond, args...; string file = __FILE__, string mod = __MODULE__) { } which could be called without explicitly having to supply __FILE__ and __MODULE__, currently you can't write such a CT logging feature with variadic parameters. (although another solution would to have __PREVIOUS_FILE__, __PREVIOUS_MODULE__, etc... that would return the file, module, line number, etc.. of what called the template or function) I think using the ';' notation is very concise and natural... just that forgetting to use it could be very problematic.
Jul 29 2013
prev sibling next sibling parent "Meta" <jared771 gmail.com> writes:
On Monday, 29 July 2013 at 17:22:50 UTC, JS wrote:
 I'm not sure how named parameters would solve the original 
 problem

Your original use case: template t(T1..., T2...) ... t!(a, b, c; d, e, f); Becomes //Some weird hypothetical syntax template t( name("T1") T1..., name("T2") T2...) { ... } t!(T1 = a, b, c, T2 = d, e, f);
 but using a syntax like what I'm suggesting one can do stuff 
 like

 ...

I think these use cases would all work with named parameters.
Jul 29 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Monday, 29 July 2013 at 17:28:57 UTC, Meta wrote:
 On Monday, 29 July 2013 at 17:22:50 UTC, JS wrote:
 I'm not sure how named parameters would solve the original 
 problem

Your original use case: template t(T1..., T2...) ... t!(a, b, c; d, e, f); Becomes //Some weird hypothetical syntax template t( name("T1") T1..., name("T2") T2...) { ... } t!(T1 = a, b, c, T2 = d, e, f);
 but using a syntax like what I'm suggesting one can do stuff 
 like

 ...

I think these use cases would all work with named parameters.

I don't think that is very robust notation but if it is then it would work. Using ';' makes it obvious the next group is starting. In your notation, it seems like there could be issues. What if T2 is a local variable, then is that an assignment? If there is no possible issues then I wouldn't mind having such a syntax... anything is better than nothing.
Jul 29 2013
prev sibling next sibling parent "Meta" <jared771 gmail.com> writes:
On Monday, 29 July 2013 at 17:35:09 UTC, JS wrote:
 I don't think that is very robust notation but if it is then it 
 would work.

 Using ';' makes it obvious the next group is starting.

I think using f(..., name = ...) is pretty obvious, and possibly easier to spot in a list of commas than `;`.
 In your notation, it seems like there could be issues. What if 
 T2 is a local variable, then is that an assignment? If there is 
 no possible issues then I wouldn't mind having such a syntax... 
 anything is better than nothing.

I don't think it would be an issue, as it would work the same way as local variables in functions that shadow those in an outer scope. import std.stdio; void main() { int x = 2; int test1(int x, int y) { return x + y; } auto n = test1(0, 1); //Prints 1 writeln(n); } As for templates, I think you currently can't define templates inside a function, so that wouldn't be an issue. As for using this notation in a template argument list, I believe it works the same way for templates. alias T1 = int; template t(T1) { alias t = T1; } void main() { //Prints "string" pragma(msg, t!string); } So the requisite variadic template: alias T1 = TypeTuple!(int, string); template t( name("T1") T1..., name("T2") T2...) { alias t = T1; } void main() { //Should print (double, char) pragma(msg, t!(T1 = double, char, T2 = bool)); //Should print (int, string) pragma(msg, T1); }
Jul 29 2013
prev sibling next sibling parent "Meta" <jared771 gmail.com> writes:
On Monday, 29 July 2013 at 17:52:41 UTC, Meta wrote:
 In your notation, it seems like there could be issues. What if 
 T2 is a local variable, then is that an assignment? If there 
 is no possible issues then I wouldn't mind having such a 
 syntax... anything is better than nothing.

I don't think it would be an issue, as it would work the same way as local variables in functions that shadow those in an outer scope. ...

Actually, I think I misunderstood your meaning. You're right, this could be a problem. Currently: import std.stdio; void main() { int x = 0; int test(int x, int y) { return x + y; } //Prints 2 writeln(test(x = 1, 1)); //Prints 1 writeln(x); } So in a hypothetical situation where we now have named parameters: import std.stdio; void main() { int x = 0; int test( name("x") x, name("y") y) { return x + y; } //Prints 3 writeln(test(x = 1, y = 2)); //What does this print? writeln(x); }
Jul 29 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Monday, 29 July 2013 at 15:02:13 UTC, Robert Clipsham wrote:
 On Monday, 29 July 2013 at 14:46:02 UTC, Robert Clipsham wrote:
 You can achieve this like so:
 ----
 template Outer(T...) {
    template Inner(U...) {
        // Do something with T and U
    }
 }
 Outer!(a, b, c).Inner!(d, e, f);
 ----

 You can see an example of it in action here (type tuple 
 intersection):
 https://github.com/mrmonday/misc/blob/master/misc/misc.d#L5

 Robert

What would be more interesting would be to have the ability to have variadic variadics, so you could use something like: ---- template Outer(T...) { template Inner(U... ...) { // Do something with T, U[0], U[1], U[2..$] } } Outer!(a, b, c).Inner!(d, e, f).Inner!(g, h, i); // Etc ---- Of course that raises the question of variadic variadic variadics and variadic variadic variadic variadics and so on, and my proposed syntax is already silly............

I don't think so, this seems similar: It would be cool if we could nest templates to get grouping template tIF(alias cond) { template tIF(alias T...) { template tIF(alias F...) { .... } } then do tIF!(cond)!(t1,t2,t3)!(f1,..,fn) which would allow for a natural grouping of variadics. The notation may seem silly but only because it is not common. which could have a short hand notation of tIF!(cond; t1, t2, t3; f1, ..., fn); (this way, if you can't keep track of your ';'s then you can use the long form)
Jul 30 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Monday, 29 July 2013 at 14:53:10 UTC, monarch_dodra wrote:
 A simple "Group" struct will solve the problem without a second 
 thought:

 struct Group(Args...)
 {
     alias Ungroup = Args;
 }

That is actually a TypeTuple that does not auto-expand. May be even worth defining it as "alias Ungroup = TypeTuple!(Args)" to be more self-documenting at cost of some redundancy. Also it does not need to be a struct, templates are not mandatory eponymous. I think it is best solution for topic starter problem in terms of ROI and worth inclusion into Phobos.
Jul 30 2013
prev sibling next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would really like this feature. I have been using struct Group(T...) { alias Ungroup = T; } but, useful as it is, this feels like something that should have some sugar on it. template A(T0 ..., T1 ...) { // use T0 & T1 } A!(int, long, double; Point, int) is so much nicer than template A(T0, T1) if(isGroup!T0 && isGroup!T1) { alias t0 = T0.Ungroup; alias t1 = T1.Ungroup; //use t0 && t1 } A!(Group!(int, long, double), Group!(Point, int))
Aug 10 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
Guys, stop feeding the stupid troll now. He is not worth anyone's time.
Aug 10 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 08/11/13 04:07, JS wrote:
 On Saturday, 10 August 2013 at 18:28:39 UTC, Artur Skawina wrote:
    A!(int, 0, float, Group!(ubyte, "m", float[2], Group!(Group!(int,11,12),
Group!(float,21,22)))) x4;

 Using just ';' would: a) be too subtle and confusing; b) not be enough
 to handle the 'x4' case above.

a) thats your opinion.

I can't think of a more subtle way to separate lists than using just a single pixel. I know -- I should try harder.
 b) wrong. Using Group is just an monstrous abuse of syntax. It's not needed. I
don't think there is a great need for nested groupings, but if so it is easy to
make ';' work.
 
 Do your really think
 
 that Group!() is any different than `;` as far as logic goes?

Yes.
 If so then you really need to learn to think abstractly.

Won't help in this case.
 This is like saying that there is a difference between using () or {} as
groupings. There is no semantic difference except what you choose to put on it.
Hell we could use ab for grouping symbols.
 
 Group!() and (); are the same except one is shorter and IMO more convienent...
there is otherwise, no other difference.

Wasn't ';' enough?
 to see this, x4 can be written as
 
 A!(int, 0, float, (ubyte, "m", float[2], ((int,11,12), (float,21,22)))) x4;
 
 Note, all I did was delete Group!. IT IS HAS THE EXACT SAME LOGICAL
INTERPRETATION.

[...]
 Just because you add some symbol in front of brackets does not magically make
anything you do different... just extra typing.
 
    A!(int, 0, float, #(ubyte, "m", float[2], #(#(int,11,12), #(float,21,22))))
x4;

But there should be no need for # as there is no need for Group. (again, I'm not talking about what D can do but what D should do)

template A(T...) { pragma(msg, T); } alias B = A!("one", (2.0, "three")); artur
Aug 11 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Saturday, 10 August 2013 at 10:19:34 UTC, John Colvin wrote:
 On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would

And this is why just because something looks "unimpressive" to you doesn't mean it is... Obviously if you've never run up against the specific problems proposed by people you won't know how frustrating it is, specially when there is an easy fix. This is why those commie bastards that shoot suggestions down at first sight ruin progress... they don't have the experience with the specific issue to know just how much it sucks.
 really like this feature. I have been using
   struct Group(T...)
   {
       alias Ungroup = T;
   }
 but, useful as it is, this feels like something that should 
 have some sugar on it.

   template A(T0 ..., T1 ...)
   {
   // use T0 & T1
   }

   A!(int, long, double; Point, int)

 is so much nicer than

EXACTLY! Why the hell would I want to go around doing the stuff below all the time when there is a perfectly fine notation(above) for it? Just because deadlinx, or dicebot, or whoever wants to be close minded about it because they themselves haven't constantly ran into the issue. It's very easy to write off someone elses issues because you yourself don't have the problems but it shows a lot of arrogance.
   template A(T0, T1)
   if(isGroup!T0 && isGroup!T1)
   {
       alias t0 = T0.Ungroup;
       alias t1 = T1.Ungroup;

       //use t0 && t1
   }

   A!(Group!(int, long, double), Group!(Point, int))

Even better, you can use something like A!(int, long, double, _S_, Point, int) where _S_ acts as the `;`. Not great, but in my opinion it is simpler than the grouping and ungrouping. (you can write a utility function to convert between them).
Aug 10 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Saturday, 10 August 2013 at 12:40:55 UTC, JS wrote:
 On Saturday, 10 August 2013 at 10:19:34 UTC, John Colvin wrote:
 On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would

And this is why just because something looks "unimpressive" to you doesn't mean it is... Obviously if you've never run up against the specific problems proposed by people you won't know how frustrating it is, specially when there is an easy fix. This is why those commie bastards that shoot suggestions down at first sight ruin progress... they don't have the experience with the specific issue to know just how much it sucks.

That is laden with assumption. I had run in to the problem before, several times. I was not initially convinced your suggestion was a good idea, especially after monarch dodra posted Group. After some more experience and thought I have now changed my mind. Anyway, never mind all that, what matters is the proposal. I think this proposal is a good idea, or at least that there should be some simple syntax for doing this sort of thing. In particular I'd be interested in hearing from bearophile about how it might fit in with his wider ideas on tuples and syntax for packing/unpacking. Also, are there any corner cases that haven't been considered. alias T = Tuple!(int, int); alias Ts = Tuple!(T, T); template A(S0..., S1...) { alias s0 = S0; alias s1 = S1; } A!(Ts.init).s0 a; what types are a/s0 and s1? s0 == Tuple!(int, int) == s1 or s0 == Tuple!(Tuple!(int, int), Tuple!(int, int)) and s1 is an empty tuple i.e. what will the rules for auto-expansion/unpacking be?
Aug 10 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Saturday, 10 August 2013 at 17:08:57 UTC, John Colvin wrote:
 On Saturday, 10 August 2013 at 12:40:55 UTC, JS wrote:
 On Saturday, 10 August 2013 at 10:19:34 UTC, John Colvin wrote:
 On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would

And this is why just because something looks "unimpressive" to you doesn't mean it is... Obviously if you've never run up against the specific problems proposed by people you won't know how frustrating it is, specially when there is an easy fix. This is why those commie bastards that shoot suggestions down at first sight ruin progress... they don't have the experience with the specific issue to know just how much it sucks.

That is laden with assumption. I had run in to the problem before, several times. I was not initially convinced your suggestion was a good idea, especially after monarch dodra posted Group. After some more experience and thought I have now changed my mind. Anyway, never mind all that, what matters is the proposal.

Well, again, the issue is that it only becomes one once you've actually experienced the problem enough. This is why those that actually haven't even run into the problem shouldn't really have a say so if it is valid or not, unless they can bring some theoretical reason why it is flawed.
 I think this proposal is a good idea, or at least that there 
 should be some simple syntax for doing this sort of thing. In 
 particular I'd be interested in hearing from bearophile about 
 how it might fit in with his wider ideas on tuples and syntax 
 for packing/unpacking.

 Also, are there any corner cases that haven't been considered.

   alias T = Tuple!(int, int);
   alias Ts = Tuple!(T, T);

   template A(S0..., S1...)
   {
       alias s0 = S0;
       alias s1 = S1;
   }

   A!(Ts.init).s0 a;

 what types are a/s0 and s1?

 s0 == Tuple!(int, int) == s1

 or

 s0 == Tuple!(Tuple!(int, int), Tuple!(int, int))
 and s1 is an empty tuple

 i.e. what will the rules for auto-expansion/unpacking be?

The way I see it, using `;` is what separates groups. So in your above code, s1 is empty. The `;` is shorthand for specifying groups. No where do you specify such a group for A!(...), so there are no groups. There is no direct way to get your second case under my proposal... unless the compiler was given some additional feature to do so. e.g., alias Ts = Tuple!(T, T); is not the same as alias Ts = Tuple!(T; T); also, we would probably want to use template A(S0...; S1...) instead of template A(S0..., S1...) The main thing is that `;` adds an extra symbol to split on that is entirely distinct from `,`. If you wanted, you could have Tuple! and TupleSC!. TupleSC! is used when splitting `;`. Tuple! and TupleSC! are not directly related(but can easily be flattened together if necessary.
Aug 10 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
  alias T = Tuple!(int, int);
  alias Ts = Tuple!(T, T);

  template A(S0..., S1...)
  {
      alias s0 = S0;
      alias s1 = S1;
  }

  A!(Ts.init).s0 a;


so alias T = Tuple!(int, int); alias Ts = TupleSC!(T, T); then Ts == ((int, int); (int, int)) and alias T = TupleSC!(int, int); alias Ts = Tuple!(T, T); (int; int), (int; int)) and alias T = TupleSC!(int, int); alias Ts = TupleSC!(T, T); (int; int); (int; int)) which all can be written longhand as TupleY!(TupleX!(int,int), TupleX!(int,int)).
Aug 10 2013
prev sibling next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 08/10/13 12:19, John Colvin wrote:
 On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single variadic but is
messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would really like this feature. I have been using struct Group(T...) { alias Ungroup = T; } but, useful as it is, this feels like something that should have some sugar on it. template A(T0 ..., T1 ...) { // use T0 & T1 } A!(int, long, double; Point, int) is so much nicer than template A(T0, T1) if(isGroup!T0 && isGroup!T1) { alias t0 = T0.Ungroup; alias t1 = T1.Ungroup; //use t0 && t1 } A!(Group!(int, long, double), Group!(Point, int))

template Tuple(A...) { alias Tuple = A; } template Ungroup(G...) if (G.length==1) { static if(__traits(compiles, Tuple!(G[0].Ungroup))) alias Ungroup = Tuple!(G[0].Ungroup); else alias Ungroup = Tuple!(G[0]); } then template A(T...) { //alias t0 = Ungroup!(T[0]); //alias t1 = Ungroup!(T[1]); //use t0 && t1 static assert( { foreach (ET; T) pragma(msg, Ungroup!ET); return 1; }()); alias A = Ungroup!(T[0])[0]; Ungroup!(T[2])[$-1] AFloat; } A!(Group!(int, long, double), Group!(Point, int), float) x; A!(int, Group!(Point, int), Group!("f", float)) x2; A!(int, Group!(Point, int), float, "blah", double, Group!(ubyte, "m", float[8], 3.14)) x3; A!(int, 0, float, Group!(ubyte, "m", float[2], Group!(Group!(int,11,12), Group!(float,21,22)))) x4; Using just ';' would: a) be too subtle and confusing; b) not be enough to handle the 'x4' case above. artur
Aug 10 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Saturday, 10 August 2013 at 18:28:39 UTC, Artur Skawina wrote:
 On 08/10/13 12:19, John Colvin wrote:
 On Monday, 29 July 2013 at 13:23:23 UTC, JS wrote:
 Sometimes it's nice to be able to have groups of variadic 
 parameters:

 template t(T1..., T2...)

 ...

 t!(a, b, c; d, e, f);

 so that a,b,c are for T1 and d,e,f are for T2.

 This can be done by making a symbol and breaking up a single 
 variadic but is messy.

 I doubt such a feature will ever get added but who knows...

I was initially unimpressed, but after some recent work I would really like this feature. I have been using struct Group(T...) { alias Ungroup = T; } but, useful as it is, this feels like something that should have some sugar on it. template A(T0 ..., T1 ...) { // use T0 & T1 } A!(int, long, double; Point, int) is so much nicer than template A(T0, T1) if(isGroup!T0 && isGroup!T1) { alias t0 = T0.Ungroup; alias t1 = T1.Ungroup; //use t0 && t1 } A!(Group!(int, long, double), Group!(Point, int))

template Tuple(A...) { alias Tuple = A; } template Ungroup(G...) if (G.length==1) { static if(__traits(compiles, Tuple!(G[0].Ungroup))) alias Ungroup = Tuple!(G[0].Ungroup); else alias Ungroup = Tuple!(G[0]); } then template A(T...) { //alias t0 = Ungroup!(T[0]); //alias t1 = Ungroup!(T[1]); //use t0 && t1 static assert( { foreach (ET; T) pragma(msg, Ungroup!ET); return 1; }()); alias A = Ungroup!(T[0])[0]; Ungroup!(T[2])[$-1] AFloat; } A!(Group!(int, long, double), Group!(Point, int), float) x; A!(int, Group!(Point, int), Group!("f", float)) x2; A!(int, Group!(Point, int), float, "blah", double, Group!(ubyte, "m", float[8], 3.14)) x3; A!(int, 0, float, Group!(ubyte, "m", float[2], Group!(Group!(int,11,12), Group!(float,21,22)))) x4; Using just ';' would: a) be too subtle and confusing; b) not be enough to handle the 'x4' case above. artur

a) thats your opinion. b) wrong. Using Group is just an monstrous abuse of syntax. It's not needed. I don't think there is a great need for nested groupings, but if so it is easy to make ';' work. Do your really think that Group!() is any different than `;` as far as logic goes? If so then you really need to learn to think abstractly. This is like saying that there is a difference between using () or {} as groupings. There is no semantic difference except what you choose to put on it. Hell we could use ab for grouping symbols. Group!() and (); are the same except one is shorter and IMO more convienent... there is otherwise, no other difference. to see this, x4 can be written as A!(int, 0, float, (ubyte, "m", float[2], ((int,11,12), (float,21,22)))) x4; Note, all I did was delete Group!. IT IS HAS THE EXACT SAME LOGICAL INTERPRETATION. Just because it is more confusing for you does mean anything except you need to try harder. In this case, there is no need to use `;` because there is no group separation(in your definition of A). I would prefer to write it as A!(int, 0, float, (ubyte, "m", float[2], (int,11,12; float,21,22))) x4; As I can see the groups easier. In this case the ; serves only to distinguish linear groupings, not nested ones as it can't do this... brackets must be used. Linear groupings(sequential groups) are what is going to be used most of the time. I did not not have the need for nesting groups so didn't use brackets but it would be something needed for some. I don't think there would be a huge reason to have nested groupings since it is generally a compile time construct but I suppose in some instances it could be useful. e.g., key value pairs: ((k1, v1), (k2, v2), ...) but even then one could just write it (k1, v1; k2, v2; ....) The main point is that what you are trying to offer does not simplify things... and simplification is what is important. D can already do what is required in a roundabout way but it requires extra work and is more verbose(which I think, in general, is bad). I'd rather have something like # instead of group. e.g., Just because you add some symbol in front of brackets does not magically make anything you do different... just extra typing.
    A!(int, 0, float, #(ubyte, "m", float[2], #(#(int,11,12), 
 #(float,21,22)))) x4;

But there should be no need for # as there is no need for Group. (again, I'm not talking about what D can do but what D should do)
Aug 10 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Sunday, 11 August 2013 at 03:14:46 UTC, Timon Gehr wrote:
 Guys, stop feeding the stupid troll now. He is not worth 
 anyone's time.

Yeah, who's the troll? Variadic grouping is useless says the troll.
Aug 10 2013
prev sibling next sibling parent "jerro" <a a.com> writes:
 Group!() and (); are the same except one is shorter and IMO 
 more convienent... there is otherwise, no other difference.

Except, of course, the fact that one requires a language change and the other doesn't.
Aug 11 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Sunday, 11 August 2013 at 08:32:35 UTC, jerro wrote:
 Group!() and (); are the same except one is shorter and IMO 
 more convienent... there is otherwise, no other difference.

Except, of course, the fact that one requires a language change and the other doesn't.

and? How many language changes were done to D to make things more convienent? I didn't ask about how to do Variadic grouping but mentioned it as a feature for D. "This can be done by making a symbol and breaking up a single variadic but is messy." Using Group is messy. I know some people like messy code but that shouldn't require everyone to write messy code.
Aug 11 2013
prev sibling parent "BS" <slackovsky gmail.com> writes:
Man I love it when this Javascript bloke posts. The tantrums are 
brilliant :-)

JS: Why does D not do X, it should and without it D is useless. 
Implement my minions!
DD: Why should X be implemented? Show us some example where it 
would be useful.
JS: You are an ignorant stupid fool if you cannot see the 
benefit. And obviously too lazy to figure out an example yourself.

Here's your dummy JS, but please keep spitting it. It gives me 
great amusement reading your posts when you've been on the bean.
Aug 11 2013