Thread overview | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
April 01, 2015 Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
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. |
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dzugaru | 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.
|
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dzugaru | 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 |
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Wednesday, 1 April 2015 at 18:13:15 UTC, Ali Çehreli wrote:
> 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
Great tip. Is that in your book somewhere?
|
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | > 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
|
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | On 04/01/2015 11:15 AM, John Colvin wrote: >> 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 > > Great tip. Is that in your book somewhere? 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. Ali |
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Wednesday, 1 April 2015 at 18:13:15 UTC, Ali Çehreli wrote:
> 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
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.
|
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dzugaru | On Wednesday, 1 April 2015 at 18:20:41 UTC, Dzugaru wrote:
>> 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 :)
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.
|
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dzugaru | 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 |
April 01, 2015 Re: Template argument deduction from a function call question | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Wednesday, 1 April 2015 at 18:37:24 UTC, Ali Çehreli wrote:
> 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
This is perfect, many thanks. Didn't know I can use "=" in template parameter list.
|
Copyright © 1999-2021 by the D Language Foundation