digitalmars.D - Bugs in template constraints
- Andrej Mitrovic (64/64) Aug 03 2010 There seem to be some bugs with template constraints. Here's a reduce ex...
- Philippe Sigaud (23/30) Aug 03 2010 I think that's because you cannot directly take the type of a statement....
- Andrej Mitrovic (6/40) Aug 03 2010 The { comes after "typeof(" as in your second example, and then it compi...
- Andrej Mitrovic (5/64) Aug 03 2010 Oh and there's a shorter way to write this example, by using isInputRang...
- Philippe Sigaud (5/9) Aug 03 2010 As I said, abstracting away common constraints is a common trick. Have a
- Andrej Mitrovic (16/26) Aug 03 2010 I haven't even noticed those.
- Pelle (5/27) Aug 03 2010 You only need to call it if you want to check the return type. You
- Andrej Mitrovic (5/53) Aug 03 2010 I guess that would make sense. With {}() I could add a comparison for th...
- Philippe Sigaud (11/16) Aug 03 2010 Yes, that's it. If the enclosed statements are OK, then the whole delega...
There seem to be some bugs with template constraints. Here's a reduce example (from TDPL) which will not compile: import std.stdio; import std.range; property bool empty(T)(T[] a) { return a.length == 0; } property ref T front(T)(T[] a) { return a[0]; } void popFront(T)(ref T[] a) { a = a[1 .. $]; } V reduce(alias fun, V, R)(V x, R range) if (is(typeof(x = fun(x, range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) { //~ writeln(is(typeof(x = fun(x, range.front)))); //~ writeln(is(typeof(range.empty) == bool)); //~ writeln(is(typeof(range.popFront()))); for ( ; !range.empty; range.popFront()) { x = fun(x, range.front); } return x; } unittest { int[] r = [10, 14, 3, 5, 23]; // compute sum int sum = reduce!((a, b) { return a + b; })(0, r); assert(sum == 55); // compute minimum int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r); assert(min == 3); } void main() { } Errors: reduce_original.d(28): Error: template reduce_original.reduce(alias fun,V,R) if (is(typeof(x = fun(x,range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) does not match any function template declaration reduce_original.d(28): Error: template reduce_original.reduce(alias fun,V,R) if (is(typeof(x = fun(x,range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) cannot deduce template function from argument types !(__dgliteral1)(int,int[]) reduce_original.d(28): Error: template instance errors instantiating template reduce_original.d(32): Error: template reduce_original.reduce(alias fun,V,R) if (is(typeof(x = fun(x,range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) does not match any function template declaration reduce_original.d(32): Error: template reduce_original.reduce(alias fun,V,R) if (is(typeof(x = fun(x,range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) cannot deduce template function from argument types !(__dgliteral4)(int,int[]) reduce_original.d(32): Error: template instance errors instantiating template If I comment out the constraints, and uncomment those writeln's - which are the same as the constraints - like so: V reduce(alias fun, V, R)(V x, R range) //~ if (is(typeof(x = fun(x, range.front))) //~ && is(typeof(range.empty) == bool) //~ && is(typeof(range.popFront()))) { writeln(is(typeof(x = fun(x, range.front)))); writeln(is(typeof(range.empty) == bool)); writeln(is(typeof(range.popFront()))); for ( ; !range.empty; range.popFront()) { x = fun(x, range.front); } return x; } I will get all true results back: true true true true true true I'm filing a bug unless something else is to blame here.
Aug 03 2010
On Tue, Aug 3, 2010 at 20:59, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:There seem to be some bugs with template constraints. Here's a reduce example (from TDPL) which will not compile: V reduce(alias fun, V, R)(V x, R range) if (is(typeof(x = fun(x, range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront())))I'm filing a bug unless something else is to blame here.I think that's because you cannot directly take the type of a statement. The assignment in the first typeof() is to blame. To make a statement into an expression, transform it into an anonymous void delegate(): put it in braces (with a semicolon at the end) and call it like a function, like this: V reduce(alias fun, V, R)(V x, R range) if (is({ typeof(x = fun(x, range.front);}())) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) {...} Or, more readable, wrap all the code you want to test into curly brackets and evaluates its global return type: V reduce(alias fun, V, R)(V x, R range) if (is(typeof({ // I want to be able to do that with an R and a V: x = fun(x, range.front); if (range.empty) {}; range.popFront(); }()))) {...} It's a D idiom you'll see in many places in the standard library. I personally find it a _bit_ heavy on parenthesis, even though I like Lisp. Philippe
Aug 03 2010
On Tue, Aug 3, 2010 at 9:34 PM, Philippe Sigaud <philippe.sigaud gmail.com>wrote:On Tue, Aug 3, 2010 at 20:59, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:The { comes after "typeof(" as in your second example, and then it compiles.There seem to be some bugs with template constraints. Here's a reduce example (from TDPL) which will not compile: V reduce(alias fun, V, R)(V x, R range) if (is(typeof(x = fun(x, range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront())))I'm filing a bug unless something else is to blame here.I think that's because you cannot directly take the type of a statement. The assignment in the first typeof() is to blame. To make a statement into an expression, transform it into an anonymous void delegate(): put it in braces (with a semicolon at the end) and call it like a function, like this: V reduce(alias fun, V, R)(V x, R range) if (is({ typeof(x = fun(x, range.front);}())) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) {...}Or, more readable, wrap all the code you want to test into curly brackets and evaluates its global return type: V reduce(alias fun, V, R)(V x, R range) if (is(typeof({ // I want to be able to do that with an R and a V: x = fun(x, range.front); if (range.empty) {}; range.popFront(); }()))) {...} It's a D idiom you'll see in many places in the standard library. I personally find it a _bit_ heavy on parenthesis, even though I like Lisp. PhilippeYeah, those paranthesis are getting a bit scary now. :) I guess this one goes to the TDPL errata. Thanks for your help Philippe.
Aug 03 2010
Oh and there's a shorter way to write this example, by using isInputRange from std.range, like so: if (isInputRange!R && is(typeof({x = fun(x, range.front);}))) This was in TDPL (except the {}'s which are missing). On Tue, Aug 3, 2010 at 10:01 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:On Tue, Aug 3, 2010 at 9:34 PM, Philippe Sigaud <philippe.sigaud gmail.comwrote:On Tue, Aug 3, 2010 at 20:59, Andrej Mitrovic <andrej.mitrovich gmail.comThe { comes after "typeof(" as in your second example, and then it compiles.wrote:There seem to be some bugs with template constraints. Here's a reduce example (from TDPL) which will not compile: V reduce(alias fun, V, R)(V x, R range) if (is(typeof(x = fun(x, range.front))) && is(typeof(range.empty) == bool) && is(typeof(range.popFront())))I'm filing a bug unless something else is to blame here.I think that's because you cannot directly take the type of a statement. The assignment in the first typeof() is to blame. To make a statement into an expression, transform it into an anonymous void delegate(): put it in braces (with a semicolon at the end) and call it like a function, like this: V reduce(alias fun, V, R)(V x, R range) if (is({ typeof(x = fun(x, range.front);}())) && is(typeof(range.empty) == bool) && is(typeof(range.popFront()))) {...}Or, more readable, wrap all the code you want to test into curly brackets and evaluates its global return type: V reduce(alias fun, V, R)(V x, R range) if (is(typeof({ // I want to be able to do that with an R and a V: x = fun(x, range.front); if (range.empty) {}; range.popFront(); }()))) {...} It's a D idiom you'll see in many places in the standard library. I personally find it a _bit_ heavy on parenthesis, even though I like Lisp. PhilippeYeah, those paranthesis are getting a bit scary now. :) I guess this one goes to the TDPL errata. Thanks for your help Philippe.
Aug 03 2010
On Tue, Aug 3, 2010 at 22:04, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:Oh and there's a shorter way to write this example, by using isInputRange from std.range, like so: if (isInputRange!R && is(typeof({x = fun(x, range.front);})))Does this work, without the () after the } ?This was in TDPL (except the {}'s which are missing).As I said, abstracting away common constraints is a common trick. Have a look at std.range, you'll see a bunch of these. Philippe
Aug 03 2010
On Tue, Aug 3, 2010 at 10:23 PM, Philippe Sigaud <philippe.sigaud gmail.com>wrote:On Tue, Aug 3, 2010 at 22:04, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:I haven't even noticed those. In the following, If I add the pair of ()'s I get void as a return type. If I remove them, I get void delegate(): writeln(typeid(typeof( delegate void () {int x = 1;}()))); // writes void writeln(typeid(typeof( delegate void () {int x = 1;}))); // writes void delegate() So I definitely need to add them. When not added the expression evaluates to void delegate(), which is a valid type and the constraint then passes. If I understood everything, this code in the constraint: is(typeof({x = fun(x, range.front);}() ))) creates an anonymous function, the compiler sees it's trying to access x so it makes it a delegate, and it infers that the function takes no arguments and the return type is void. Did I get this right? This was in TDPL (except the {}'s which are missing).Oh and there's a shorter way to write this example, by using isInputRange from std.range, like so: if (isInputRange!R && is(typeof({x = fun(x, range.front);})))Does this work, without the () after the } ?As I said, abstracting away common constraints is a common trick. Have a look at std.range, you'll see a bunch of these. Philippe
Aug 03 2010
On 08/03/2010 11:07 PM, Andrej Mitrovic wrote:On Tue, Aug 3, 2010 at 10:23 PM, Philippe Sigaud <philippe.sigaud gmail.com <mailto:philippe.sigaud gmail.com>> wrote: On Tue, Aug 3, 2010 at 22:04, Andrej Mitrovic <andrej.mitrovich gmail.com <mailto:andrej.mitrovich gmail.com>> wrote: Oh and there's a shorter way to write this example, by using isInputRange from std.range, like so: if (isInputRange!R && is(typeof({x = fun(x, range.front);}))) Does this work, without the () after the } ? I haven't even noticed those. In the following, If I add the pair of ()'s I get void as a return type. If I remove them, I get void delegate(): writeln(typeid(typeof( delegate void () {int x = 1;}()))); // writes void writeln(typeid(typeof( delegate void () {int x = 1;}))); // writes void delegate() So I definitely need to add them. When not added the expression evaluates to void delegate(), which is a valid type and the constraint then passes. If I understood everything, this code in the constraint: is(typeof({x = fun(x, range.front);}() ))) creates an anonymous function, the compiler sees it's trying to access x so it makes it a delegate, and it infers that the function takes no arguments and the return type is void. Did I get this right?You only need to call it if you want to check the return type. You cannot create a function with content that can't compile, so in this case, the () isn't needed. Correct me if I'm wrong, of course. :)
Aug 03 2010
I guess that would make sense. With {}() I could add a comparison for the return type if I ever needed that. I did take a look in std.range, and pretty much all the templates there use the {}() syntax. On Tue, Aug 3, 2010 at 11:53 PM, Pelle <pelle.mansson gmail.com> wrote:On 08/03/2010 11:07 PM, Andrej Mitrovic wrote:On Tue, Aug 3, 2010 at 10:23 PM, Philippe Sigaud <philippe.sigaud gmail.com <mailto:philippe.sigaud gmail.com>> wrote: On Tue, Aug 3, 2010 at 22:04, Andrej Mitrovic <andrej.mitrovich gmail.com <mailto:andrej.mitrovich gmail.com>> wrote: Oh and there's a shorter way to write this example, by using isInputRange from std.range, like so: if (isInputRange!R && is(typeof({x = fun(x, range.front);}))) Does this work, without the () after the } ? I haven't even noticed those. In the following, If I add the pair of ()'s I get void as a return type. If I remove them, I get void delegate(): writeln(typeid(typeof( delegate void () {int x = 1;}()))); // writes void writeln(typeid(typeof( delegate void () {int x = 1;}))); // writes void delegate() So I definitely need to add them. When not added the expression evaluates to void delegate(), which is a valid type and the constraint then passes. If I understood everything, this code in the constraint: is(typeof({x = fun(x, range.front);}() ))) creates an anonymous function, the compiler sees it's trying to access x so it makes it a delegate, and it infers that the function takes no arguments and the return type is void. Did I get this right?You only need to call it if you want to check the return type. You cannot create a function with content that can't compile, so in this case, the () isn't needed. Correct me if I'm wrong, of course. :)
Aug 03 2010
On Tue, Aug 3, 2010 at 23:07, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:If I understood everything, this code in the constraint: is(typeof({x = fun(x, range.front);}() ))) creates an anonymous function, the compiler sees it's trying to access x so it makes it a delegate, and it infers that the function takes no arguments and the return type is void. Did I get this right?Yes, that's it. If the enclosed statements are OK, then the whole delegate compiles, gets a type (void delegate() ), and so on, though is(typeof()). So, using it as a way to get template constraints to work on many statements was not planned that way, I think. But it works :) Basically any D block statement: { statements;} can be seen as a void delegate(). see http://www.digitalmars.com/d/2.0/lazy-evaluation.html and http://www.digitalmars.com/d/2.0/statement.html#ScopeStatement for an example of this. Philippe
Aug 03 2010