digitalmars.D - Template argument deduction from a function call question
- Dzugaru (29/29) Apr 01 2015 Following recent IRC discussion.
- John Colvin (11/40) Apr 01 2015 a)
- Dzugaru (9/19) Apr 01 2015 a) Had a look at std.algorithm.reduce, looks like this is what I
- John Colvin (9/30) Apr 01 2015 Yeah it's not the easiest to understand at first glance. The big
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (14/18) Apr 01 2015 I can't explain exactly why that doesn't work.
- John Colvin (2/22) Apr 01 2015 Great tip. Is that in your book somewhere?
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (7/21) Apr 01 2015 I don't think so; not that one... (Noted though; I may get to it.)
- Dzugaru (6/26) Apr 01 2015 This code does work when you provide second (non-default)
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (21/26) Apr 01 2015 Another attempt:
- Dzugaru (3/35) Apr 01 2015 This is perfect, many thanks. Didn't know I can use "=" in
- Steven Schveighoffer (11/53) Apr 02 2015 I'd still put the template constraint in: if(is(E == ElementType!S)),
Following recent IRC discussion. I want to write a generic list aggregate function that works with builtin types like int[] as well as custom classes/structs that define front, empty, popFront: import std.range; ElementType!S aggregate(alias func, S)(S list, ElementType!S accum = ElementType!S.init) if(is(typeof(func(accum, accum)) == typeof(accum))) { foreach(ref e; list) { accum = func(accum, e); } return accum; } Now I use it: auto min1 = aggregate!((a, b) { return a < b ? a : b; }, int[])([2,4,1,3,5], int.max); auto min2 = aggregate!((a, b) { return a < b ? a : b; }, MyRange)(new MyRange(), int.max); That works ok. Now I don't want to specify template argument S, it can be deduced from the function call, right? auto min1 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5], int.max); Doesn't work! "Error: template math.aggregate cannot deduce function from argument types..." Now try this: //No second parameter - leave it to be default auto sum = aggregate!((a, b) { return a + b; })([2,4,1,3,5]); And it deduces S just fine.
Apr 01 2015
On Wednesday, 1 April 2015 at 17:57:12 UTC, Dzugaru wrote:Following recent IRC discussion. I want to write a generic list aggregate function that works with builtin types like int[] as well as custom classes/structs that define front, empty, popFront: import std.range; ElementType!S aggregate(alias func, S)(S list, ElementType!S accum = ElementType!S.init) if(is(typeof(func(accum, accum)) == typeof(accum))) { foreach(ref e; list) { accum = func(accum, e); } return accum; } Now I use it: auto min1 = aggregate!((a, b) { return a < b ? a : b; }, int[])([2,4,1,3,5], int.max); auto min2 = aggregate!((a, b) { return a < b ? a : b; }, MyRange)(new MyRange(), int.max); That works ok. Now I don't want to specify template argument S, it can be deduced from the function call, right? auto min1 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5], int.max); Doesn't work! "Error: template math.aggregate cannot deduce function from argument types..." Now try this: //No second parameter - leave it to be default auto sum = aggregate!((a, b) { return a + b; })([2,4,1,3,5]); And it deduces S just fine.a) isn't this almost, if not exactly, the same as std.algorithm.reduce? b) you can write nice things like this: auto min = [2,4,1,3,5].aggregate!((a, b) => a < b ? a : b)(int.max); c) the deduction failure looks like a bug to me, perhaps there is a good reason why it can't work in the general case.
Apr 01 2015
a) isn't this almost, if not exactly, the same as std.algorithm.reduce? b) you can write nice things like this: auto min = [2,4,1,3,5].aggregate!((a, b) => a < b ? a : b)(int.max); c) the deduction failure looks like a bug to me, perhaps there is a good reason why it can't work in the general case.a) Had a look at std.algorithm.reduce, looks like this is what I did, but there are 2 overloads inside a template block instead of parameter with a default value (also less strictly typed): template reduce(fun...) { auto reduce(R)(R r) auto reduce(S, R)(S seed, R r) } The code there though is mindboggling :) b) Thanks, forgot about that
Apr 01 2015
On Wednesday, 1 April 2015 at 18:20:41 UTC, Dzugaru wrote:Yeah it's not the easiest to understand at first glance. The big differences are that it works with multiple functions in one pass, has it's seed and range the wrong way around for UFCS (grrrr!!!) and works with any seed type that can have the result of the function(s) assigned to it, which is a lot more flexible than insisting on using the element type.* *One really obvious example is doing a sum in to a larger variable so as to avoid overflow.a) isn't this almost, if not exactly, the same as std.algorithm.reduce? b) you can write nice things like this: auto min = [2,4,1,3,5].aggregate!((a, b) => a < b ? a : b)(int.max); c) the deduction failure looks like a bug to me, perhaps there is a good reason why it can't work in the general case.a) Had a look at std.algorithm.reduce, looks like this is what I did, but there are 2 overloads inside a template block instead of parameter with a default value (also less strictly typed): template reduce(fun...) { auto reduce(R)(R r) auto reduce(S, R)(S seed, R r) } The code there though is mindboggling :)
Apr 01 2015
On 04/01/2015 10:57 AM, Dzugaru wrote:ElementType!S aggregate(alias func, S)(S list, ElementType!S accum = ElementType!S.init) if(is(typeof(func(accum, accum)) == typeof(accum))) {[...]}I can't explain exactly why that doesn't work. However, I've discovered a number of times that reducing the dependency between template parameters helps. Instead of using ElementType!S in the parameter list, introduce a third one (E), which you check in the template constraint: ElementType!S aggregate(alias func, S, E)(S list, E accum = E.init) if(is (E == ElementType!S) && // <-- ADDED is(typeof(func(accum, accum)) == typeof(accum))) { // ... } Now it works. Ali
Apr 01 2015
On Wednesday, 1 April 2015 at 18:13:15 UTC, Ali Çehreli wrote:On 04/01/2015 10:57 AM, Dzugaru wrote:Great tip. Is that in your book somewhere?ElementType!S aggregate(alias func, S)(S list, ElementType!Saccum =ElementType!S.init) if(is(typeof(func(accum, accum)) == typeof(accum))) {[...]}I can't explain exactly why that doesn't work. However, I've discovered a number of times that reducing the dependency between template parameters helps. Instead of using ElementType!S in the parameter list, introduce a third one (E), which you check in the template constraint: ElementType!S aggregate(alias func, S, E)(S list, E accum = E.init) if(is (E == ElementType!S) && // <-- ADDED is(typeof(func(accum, accum)) == typeof(accum))) { // ... } Now it works. Ali
Apr 01 2015
On 04/01/2015 11:15 AM, John Colvin wrote:I don't think so; not that one... (Noted though; I may get to it.) I had carefully tried to avoid adding "too much" information. However, as the book progressed added more and more information like that, especially after Luís Marques's recommendations. I am very grateful for the effort he has put into reviewing and editing the book. AliInstead of using ElementType!S in the parameter list, introduce a third one (E), which you check in the template constraint: ElementType!S aggregate(alias func, S, E)(S list, E accum = E.init) if(is (E == ElementType!S) && // <-- ADDED is(typeof(func(accum, accum)) == typeof(accum))) { // ... } Now it works. AliGreat tip. Is that in your book somewhere?
Apr 01 2015
On Wednesday, 1 April 2015 at 18:13:15 UTC, Ali Çehreli wrote:On 04/01/2015 10:57 AM, Dzugaru wrote:This code does work when you provide second (non-default) argument to function, and doesn't if you do not (no way it can deduce E solely from checks I assume). My version, in constract, works when you do not provide second argument and doesn't if you do.ElementType!S aggregate(alias func, S)(S list, ElementType!Saccum =ElementType!S.init) if(is(typeof(func(accum, accum)) == typeof(accum))) {[...]}I can't explain exactly why that doesn't work. However, I've discovered a number of times that reducing the dependency between template parameters helps. Instead of using ElementType!S in the parameter list, introduce a third one (E), which you check in the template constraint: ElementType!S aggregate(alias func, S, E)(S list, E accum = E.init) if(is (E == ElementType!S) && // <-- ADDED is(typeof(func(accum, accum)) == typeof(accum))) { // ... } Now it works. Ali
Apr 01 2015
On 04/01/2015 11:27 AM, Dzugaru wrote:This code does work when you provide second (non-default) argument to function, and doesn't if you do not (no way it can deduce E solely from checks I assume). My version, in constract, works when you do not provide second argument and doesn't if you do.Another attempt: import std.range; ElementType!S aggregate(alias func, S, E = ElementType!S)(S list, E accum = E.init) if(is(typeof(func(accum, accum)) == typeof(accum))) { foreach(ref e; list) { accum = func(accum, e); } return accum; } void main() { auto min1 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5], int.max); auto min2 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5]); assert(min1 == 1); assert(min2 == 0); } Ali
Apr 01 2015
On Wednesday, 1 April 2015 at 18:37:24 UTC, Ali Çehreli wrote:On 04/01/2015 11:27 AM, Dzugaru wrote:This is perfect, many thanks. Didn't know I can use "=" in template parameter list.This code does work when you provide second (non-default)argument tofunction, and doesn't if you do not (no way it can deduce Esolely fromchecks I assume). My version, in constract, works when you do not providesecond argumentand doesn't if you do.Another attempt: import std.range; ElementType!S aggregate(alias func, S, E = ElementType!S)(S list, E accum = E.init) if(is(typeof(func(accum, accum)) == typeof(accum))) { foreach(ref e; list) { accum = func(accum, e); } return accum; } void main() { auto min1 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5], int.max); auto min2 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5]); assert(min1 == 1); assert(min2 == 0); } Ali
Apr 01 2015
On 4/1/15 2:46 PM, Dzugaru wrote:On Wednesday, 1 April 2015 at 18:37:24 UTC, Ali Çehreli wrote:I'd still put the template constraint in: if(is(E == ElementType!S)), because using = in the template parameter list does not constrain it to that type, it just gives a default. However, you could also change the function signature to allow more flexibility: auto aggregate(alias func, S, E = ElementType!S)(S list, E accum = E.init) if(is(typeof(accum = func(accum, ElementType!S.init)))) But then again, you can use reduce ;) -SteveOn 04/01/2015 11:27 AM, Dzugaru wrote:This is perfect, many thanks. Didn't know I can use "=" in template parameter list.This code does work when you provide second (non-default)argument tofunction, and doesn't if you do not (no way it can deduce Esolely fromchecks I assume). My version, in constract, works when you do not providesecond argumentand doesn't if you do.Another attempt: import std.range; ElementType!S aggregate(alias func, S, E = ElementType!S)(S list, E accum = E.init) if(is(typeof(func(accum, accum)) == typeof(accum))) { foreach(ref e; list) { accum = func(accum, e); } return accum; } void main() { auto min1 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5], int.max); auto min2 = aggregate!((a, b) { return a < b ? a : b; })([2,4,1,3,5]); assert(min1 == 1); assert(min2 == 0); } Ali
Apr 02 2015