Thread overview | ||||||
---|---|---|---|---|---|---|
|
November 30, 2010 Passing functions to functionals | ||||
---|---|---|---|---|
| ||||
In my library I have a lot of functionals (functions that take other functions as parameters). Here is an example that shows the style I use to define them: // Example: Evaluate the function/delegate/functor f at x. auto eval(F, X)(F f, X x) { return f(x); } // Test void main() { int add2(int i) { return i + 2; } assert (eval(&add2, 1) == 3); } In other words, the function is passed as a run-time parameter. I've seen this (or similar) style used in Phobos, but there, I've also noted that functions are sometimes passed as template alias parameters: // Same as above, using template alias parameter. auto eval(alias f, X)(X x) { return f(x); } // Test void main() { int add2(int i) { return i + 2; } assert (eval!add2(1) == 3); } I'd be grateful if people would share their knowledge of the pros and cons of each method. For instance, are there any situations where template alias parameters don't work? Thanks, -Lars |
November 30, 2010 Re: Passing functions to functionals | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | On 30.11.2010 14:59, Lars T. Kyllingstad wrote: > In my library I have a lot of functionals (functions that take other > functions as parameters). Here is an example that shows the style I use > to define them: > > // Example: Evaluate the function/delegate/functor f at x. > auto eval(F, X)(F f, X x) { return f(x); } > > // Test > void main() > { > int add2(int i) { return i + 2; } > assert (eval(&add2, 1) == 3); > } > > In other words, the function is passed as a run-time parameter. I've > seen this (or similar) style used in Phobos, but there, I've also noted > that functions are sometimes passed as template alias parameters: > > // Same as above, using template alias parameter. > auto eval(alias f, X)(X x) { return f(x); } > > // Test > void main() > { > int add2(int i) { return i + 2; } > assert (eval!add2(1) == 3); > } > > I'd be grateful if people would share their knowledge of the pros and > cons of each method. For instance, are there any situations where > template alias parameters don't work? > > > Thanks, > > -Lars alias parameters must be know/computable at compile time, quick example on what you can't do: import std.stdio; auto eval(F, X)(F f, X x) { return f(x); } auto eval2(alias f,X)(X x){ return f(x); } auto summator(int k){ int f(int val){ return k + val; } return &f; } // Test void main() { int add2(int i) { return i + 2; } int traceAdd2(int i){ writeln(i," --> ",i+2); return i+2; } int delegate(int) getAdd2(){ writeln("Getting add2"); return &add2; } assert(eval(&add2, 1) == 3); assert(eval2!add2(1) == 3); assert(eval(summator(2),1) == 3); //Next one fails with //Error: closures are not yet supported in CTFE //Error: cannot evaluate summator(2) at compile time //assert(eval2!(summator(2))(1) == 3); assert(eval(&traceAdd2,1) == 3); assert(eval2!traceAdd2(1) == 3); //side effect in call is no problem assert(eval(getAdd2,1) == 3); //Next one fails with //Error: cannot evaluate writeln("Getting add2") at compile time //Error: cannot evaluate getAdd2() at compile time //assert(eval2!(getAdd2())(1) == 3); } Well, once the limitations of CTFE are dealt with, the only problem alias param not capable of would be user input parameters, and side-effects during delegate/function construction. IMHO this can pose a problem with some algorithms in std.algorithm, like _filter_ing on user provided criteria currently impossible (since predicate parameter to filter has to be known at compile time). One soultion might be is somehow provide alternative versions of the with the first version of syntax. A distinctive feature of alias parameters - they are not passed as arguments at all, as the (stripped) asm suggests: for eval2!add2(1): _TEXT:00405AB4 enter 4, 0 _TEXT:00405AB8 mov [ebp+var_4], eax _TEXT:00405ABB push [ebp+arg_0] _TEXT:00405ABE mov eax, [ebp+var_4] _TEXT:00405AC1 call test_e@main@add2; direct call, compiler knows what the alias is _TEXT:00405AC6 leave _TEXT:00405AC7 retn 4 eval(&add2, 1): _TEXT:00405A98 var_4 = dword ptr -4 _TEXT:00405A98 arg_0 = dword ptr 8 _TEXT:00405A98 arg_4 = dword ptr 0Ch _TEXT:00405A98 _TEXT:00405A98 enter 4, 0 _TEXT:00405A9C push ebx _TEXT:00405A9D mov [ebp+var_4], eax _TEXT:00405AA0 push [ebp+var_4] _TEXT:00405AA3 mov eax, [ebp+arg_0] _TEXT:00405AA6 mov edx, [ebp+arg_4] _TEXT:00405AA9 mov ebx, [ebp+arg_0] _TEXT:00405AAC call edx ;<---- note the indirect call _TEXT:00405AAE pop ebx _TEXT:00405AAF leave _TEXT:00405AB0 retn 8 That's as far as I can get for now, there is still room for investigation. -- Dmitry Olshansky |
November 30, 2010 Re: Passing functions to functionals | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | Lars T. Kyllingstad Wrote: > // Same as above, using template alias parameter. > auto eval(alias f, X)(X x) { return f(x); } > > // Test > void main() > { > int add2(int i) { return i + 2; } > assert (eval!add2(1) == 3); > } > > I'd be grateful if people would share their knowledge of the pros and cons of each method. For instance, are there any situations where template alias parameters don't work? > > > Thanks, > > -Lars Well, with using an alias parameter then std.functional.unaryFun/binaryFun is usable so you could use the eval!"a+2"(5) syntax. |
December 01, 2010 Re: Passing functions to functionals | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On Tue, 30 Nov 2010 18:49:56 +0300, Dmitry Olshansky wrote:
> On 30.11.2010 14:59, Lars T. Kyllingstad wrote:
>> In my library I have a lot of functionals (functions that take other functions as parameters). Here is an example that shows the style I use to define them:
>>
>> // Example: Evaluate the function/delegate/functor f at x. auto
>> eval(F, X)(F f, X x) { return f(x); }
>>
>> // Test
>> void main()
>> {
>> int add2(int i) { return i + 2; }
>> assert (eval(&add2, 1) == 3);
>> }
>>
>> In other words, the function is passed as a run-time parameter. I've seen this (or similar) style used in Phobos, but there, I've also noted that functions are sometimes passed as template alias parameters:
>>
>> // Same as above, using template alias parameter. auto eval(alias
>> f, X)(X x) { return f(x); }
>>
>> // Test
>> void main()
>> {
>> int add2(int i) { return i + 2; }
>> assert (eval!add2(1) == 3);
>> }
>>
>> I'd be grateful if people would share their knowledge of the pros and cons of each method. For instance, are there any situations where template alias parameters don't work?
>>
>>
>> Thanks,
>>
>> -Lars
> alias parameters must be know/computable at compile time, quick example on what you can't do:
>
> import std.stdio;
>
> auto eval(F, X)(F f, X x) { return f(x); } auto eval2(alias f,X)(X x){
> return f(x); }
>
> auto summator(int k){
> int f(int val){
> return k + val;
> }
> return &f;
> }
>
>
> // Test
> void main()
> {
> int add2(int i) { return i + 2; }
> int traceAdd2(int i){
> writeln(i," --> ",i+2);
> return i+2;
> }
> int delegate(int) getAdd2(){
> writeln("Getting add2");
> return &add2;
> }
> assert(eval(&add2, 1) == 3);
> assert(eval2!add2(1) == 3);
> assert(eval(summator(2),1) == 3);
> //Next one fails with
> //Error: closures are not yet supported in CTFE //Error: cannot
> evaluate summator(2) at compile time
> //assert(eval2!(summator(2))(1) == 3);
>
> assert(eval(&traceAdd2,1) == 3);
> assert(eval2!traceAdd2(1) == 3); //side effect in call is no
> problem
>
> assert(eval(getAdd2,1) == 3);
> //Next one fails with
> //Error: cannot evaluate writeln("Getting add2") at compile time
> //Error: cannot evaluate getAdd2() at compile time
> //assert(eval2!(getAdd2())(1) == 3);
> }
That's a very good point, and pretty much settles it for me. I'll stay away from alias parameters, then. Thanks!
-Lars
|
Copyright © 1999-2021 by the D Language Foundation