digitalmars.D.learn - How To: Passing curried functions around
- Bahman Movaqar (33/33) Sep 06 2015 I'm just learning D, so please bear with me if I'm asking
- welkam (6/6) Sep 06 2015 I dont know much about functional programming, but what stops you
- Bahman Movaqar (18/24) Sep 06 2015 Nothing specific to FP here. I simply do not want to share the
- welkam (2/2) Sep 06 2015 Now its clearer to me. You want delegates
- Meta (6/6) Sep 06 2015 The name validator_t is not idiomatic in D. Something like
- Bahman Movaqar (11/17) Sep 07 2015 Good. Now the questions is what should be the signature of the
- =?UTF-8?Q?Ali_=c3=87ehreli?= (49/76) Sep 10 2015 There is the relatively newer alias syntax which is more intuitive:
- Bahman Movaqar (12/70) Sep 11 2015 Ah...now I understand the mysterious compiler errors.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (40/89) Sep 11 2015 I was wrong there: 'partial' does take its arguments as alias template
- Bahman Movaqar (4/16) Sep 11 2015 I read "alias and with" chapter of the book to understand this
- =?UTF-8?Q?Ali_=c3=87ehreli?= (6/22) Sep 11 2015 Sorry for the confusion. :) The same keyword has a different use with
- =?UTF-8?Q?Ali_=c3=87ehreli?= (4/6) Sep 11 2015 And the official documentation:
- Bahman Movaqar (2/7) Sep 12 2015 Thanks again!
I'm just learning D, so please bear with me if I'm asking something naive. Consider the following code skeleton: // in part A of the application... // ------------------------------------------------------------- alias bool function(int n) validator_t; bool isEven(int n) { ... } bool isPrime(int n) { ... } /** * keeps asking for an int from the user until it passes * the given validator. */ int readInt(string prompt, validator_t validator) { ... } // in part B of the application which knows nothing about part A // ------------------------------------------------------------- /** * does something that involves reading an integer from input */ void foo(intReader_t reader) { ... } I'm trying to pass curried versions of `readInt` to `foo`. Obviously, I don't wish part B to know about the details of a "reader" nor I'd like to pass `prompt` and `validator` to `foo` (this really doesn't concern `foo` at all). I see that the solution is using `partial` from `std.functional`; for example: partial!(partial!(readInt, "Enter an integer:"), &isEven) However, I'm not sure if this is correct (let alone idiomatic!) and even if this is the correct way of currying `readInt`, what should be the signature of `foo`? I'd appreciate any help/hint on this.
Sep 06 2015
I dont know much about functional programming, but what stops you defining int readInt(string prompt, validator_t validator) { ... } as a free standing function and just call it from both parts of your code? What is the benefit of indirection that you create when passing function pointer?
Sep 06 2015
On Sunday, 6 September 2015 at 19:22:41 UTC, welkam wrote:I dont know much about functional programming, but what stops you defining int readInt(string prompt, validator_t validator) { ... } as a free standing function and just call it from both parts of your code? What is the benefit of indirection that you create when passing function pointer?Nothing specific to FP here. I simply do not want to share the details of reading an integer with part B. Imagine the same thing done with classes: // part A // class IntReader { string prompt; validator_t validator; int read() const { ... } } // part B // auto ir = new IntReader(...); foo(ir); This way, all `foo` needs to know is calling `read` to get an integer. I'm trying to achieve the same thing here with functions, if possible.
Sep 06 2015
Now its clearer to me. You want delegates http://wiki.dlang.org/Function_literals
Sep 06 2015
The name validator_t is not idiomatic in D. Something like ValidatorFun should be preferred. Same for intReader_t; ReadIntFun is probably preferred, or even IntReader (but that would imply that it's a struct/class in my mind). As for the actual use of partial, it's perfectly fine and idiomatic to use.
Sep 06 2015
On Monday, 7 September 2015 at 03:55:01 UTC, Meta wrote:The name validator_t is not idiomatic in D. Something like ValidatorFun should be preferred. Same for intReader_t; ReadIntFun is probably preferred, or even IntReader (but that would imply that it's a struct/class in my mind).Noted. Thanks.As for the actual use of partial, it's perfectly fine and idiomatic to use.Good. Now the questions is what should be the signature of the receiving functions if I'm going to pass the (double) curried function around? For example, considering the original piece of code, if I do alias partial!(partial!(readInt, "Enter an integer:"), &isEven) ReaderFun; is the following a valid signature for `foo`, to which I'll pass a `ReaderFun`? void foo((int delegate() readerFun)
Sep 07 2015
On 09/06/2015 12:05 PM, Bahman Movaqar wrote:alias bool function(int n) validator_t;There is the relatively newer alias syntax which is more intuitive: alias Validator = bool function(int n);bool isEven(int n) { ... } bool isPrime(int n) { ... } /** * keeps asking for an int from the user until it passes * the given validator. */ int readInt(string prompt, validator_t validator) { ... } // in part B of the application which knows nothing about part A // ------------------------------------------------------------- /** * does something that involves reading an integer from input */ void foo(intReader_t reader) { ... } I'm trying to pass curried versions of `readInt` to `foo`. Obviously, I don't wish part B to know about the details of a "reader" nor I'd like to pass `prompt` and `validator` to `foo` (this really doesn't concern `foo` at all). I see that the solution is using `partial` from `std.functional`; for example: partial!(partial!(readInt, "Enter an integer:"), &isEven) However, I'm not sure if this is correctThat does not compile because partial takes the function arguments as 'value template parameters'. Unfortunately, a function pointer like &isEven cannot be 'value template parameters'; only fundamental types and strings can... So, 'partial' is not an option for this problem.(let alone idiomatic!)Idiomatic D uses templates and passes behavior in the form of 'alias template parameters.' Alias template parameters are the convenient way of allowing anything that is callable, not just functions. For example, foo() would be much more useful if it allowed a delegate or a class object that has an overloaded opCall() operator.and even if this is the correct way of currying `readInt`, what should be the signature of `foo`?I think 'int delegate()' would do because all foo needs is a function that returns an int. The idiomatic way is to leave it as a template parameter. However, this has the problem of making each instantiation of the foo template a different type, making them incompatible to be elements of the same array: [ &(foo!myReader), &(foo!yourReader) ] // <-- compilation error One solution is to do what std.parallelism.task does: to provide both a foo template and another foo function that takes an 'int delegate()': http://ddili.org/ders/d.en/parallelism.html (Search for "The task function above has been specified as a template parameter" on that page.)I'd appreciate any help/hint on this.Here is a solution: import std.stdio; bool isEven(int n) { return !(n % 2); } int readValidInt(alias validator)(string prompt) { while (true) { int i; write(prompt); readf(" %s", &i); if (validator(i)) { return i; } else { writeln("Sorry, that's not acceptable"); } } } void foo(alias reader)() { reader(); } void main() { auto reader = () => readValidInt!isEven("Enter an integer: "); foo!reader(); } You can add template constraints to make the code easier to use. Ali
Sep 10 2015
On Friday, 11 September 2015 at 06:14:18 UTC, Ali Çehreli wrote:On 09/06/2015 12:05 PM, Bahman Movaqar wrote:Great. This is easily read by my eyes.alias bool function(int n) validator_t;There is the relatively newer alias syntax which is more intuitive: alias Validator = bool function(int n);partial takes the function arguments as 'value template parameters'. Unfortunately, a function pointer like &isEven cannot be 'value template parameters'; only fundamental types and strings can... So, 'partial' is not an option for this problem.Ah...now I understand the mysterious compiler errors.Idiomatic D uses templates and passes behavior in the form of 'alias template parameters.' Alias template parameters are the convenient way of allowing anything that is callable, not just functions. For example, foo() would be much more useful if it allowed a delegate or a class object that has an overloaded opCall() operator.True. I guess I was just pushing D functions too far.That's correct. I realised this subtle point later on, but had already switched to a `struct`y approach.and even if this is the correct way of currying `readInt`, what shouldbe thesignature of `foo`?I think 'int delegate()' would do because all foo needs is a function that returns an int.The idiomatic way is to leave it as a template parameter. However, this has the problem of making each instantiation of the foo template a different type, making them incompatible to be elements of the same array: [ &(foo!myReader), &(foo!yourReader) ] // <-- compilation error One solution is to do what std.parallelism.task does: to provide both a foo template and another foo function that takes an 'int delegate()': http://ddili.org/ders/d.en/parallelism.html (Search for "The task function above has been specified as a template parameter" on that page.)Actually, I *am* using your book (which is, by the way, very well written) plus the language specs to learn D. However, that section yet too advance for me to touch :-)import std.stdio; bool isEven(int n) { return !(n % 2); } int readValidInt(alias validator)(string prompt) { while (true) { int i; write(prompt); readf(" %s", &i); if (validator(i)) { return i; } else { writeln("Sorry, that's not acceptable"); } } } void foo(alias reader)() { reader(); } void main() { auto reader = () => readValidInt!isEven("Enter an integer: "); foo!reader(); } You can add template constraints to make the code easier to use.Clean one! Even though I don't know what's going behind the scenes, I can easily read and understand it. Thanks for the help.
Sep 11 2015
On 09/11/2015 02:41 AM, Bahman Movaqar wrote:On Friday, 11 September 2015 at 06:14:18 UTC, Ali Çehreli wrote:I was wrong there: 'partial' does take its arguments as alias template parameters but it still doesn't work probably because of a compiler or language issue: import std.functional; alias Validator = bool function(int); bool condition(int) { return true; } void foo(Validator validator) { validator(42); } void main() { alias f = partial!(foo, condition); // <-- ERROR } /usr/include/dmd/phobos/std/functional.d(662): Error: function deneme.condition (int _param_0) is not callable using argument types () /usr/include/dmd/phobos/std/functional.d(662): Error: cannot return non-void from void function I wouldn't expect an actual call for that line of code but the compiler thinks that there is a call to condition() without a parameter. (This may be related to a syntax issue caused by no-parameter functions being called without parentheses.)partial takes the function arguments as 'value template parameters'. Unfortunately, a function pointer like &isEven cannot be 'value template parameters'; only fundamental types and strings can... So, 'partial' is not an option for this problem.Ah...now I understand the mysterious compiler errors.Not necessarily. You can do the same thing with function pointers as well but I don't think 'partial' has much to offer over a lambda. I am guessing that partial predates lambdas. (?)Idiomatic D uses templates and passes behavior in the form of 'alias template parameters.' Alias template parameters are the convenient way of allowing anything that is callable, not just functions. For example, foo() would be much more useful if it allowed a delegate or a class object that has an overloaded opCall() operator.True. I guess I was just pushing D functions too far.readValidInt() is a function template that takes two information: 1) The validator as its alias template parameter. alias template parameter allows it to work with anything that can be called. 2) The prompt as its function parameter.http://ddili.org/ders/d.en/parallelism.html (Search for "The task function above has been specified as a template parameter" on that page.)Actually, I *am* using your book (which is, by the way, very well written) plus the language specs to learn D. However, that section yet too advance for me to touch :-)import std.stdio; bool isEven(int n) { return !(n % 2); } int readValidInt(alias validator)(string prompt) {The should be obvious.while (true) { int i; write(prompt); readf(" %s", &i); if (validator(i)) { return i; } else { writeln("Sorry, that's not acceptable"); } } }Another function template that takes a callable entity and calls it.void foo(alias reader)() { reader(); }The syntax above creates a lambda with this definition: Call readValidInt!isEven with the string argument "Enter an integer: ".void main() { auto reader = () => readValidInt!isEven("Enter an integer: ");foo takes that lambda. When foo eventually calls the lambda, readValidInt!isEven("Enter an integer: ") will be called.foo!reader();Ali} You can add template constraints to make the code easier to use.Clean one! Even though I don't know what's going behind the scenes, I can easily read and understand it. Thanks for the help.
Sep 11 2015
On Friday, 11 September 2015 at 18:39:15 UTC, Ali Çehreli wrote:I read "alias and with" chapter of the book to understand this but couldn't find any such use for `alias` there. Does using `alias` instead of a type makes the parameter a `callable`?readValidInt() is a function template that takes two information: 1) The validator as its alias template parameter. alias template parameter allows it to work with anything that can be called.import std.stdio; bool isEven(int n) { return !(n % 2); } int readValidInt(alias validator)(string prompt) {
Sep 11 2015
On 09/11/2015 01:07 PM, Bahman Movaqar wrote:On Friday, 11 September 2015 at 18:39:15 UTC, Ali Çehreli wrote:Sorry for the confusion. :) The same keyword has a different use with templates: http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.alias,%20template%20parameter AliI read "alias and with" chapter of the book to understand this but couldn't find any such use for `alias` there. Does using `alias` instead of a type makes the parameter a `callable`?readValidInt() is a function template that takes two information: 1) The validator as its alias template parameter. alias template parameter allows it to work with anything that can be called.import std.stdio; bool isEven(int n) { return !(n % 2); } int readValidInt(alias validator)(string prompt) {
Sep 11 2015
On 09/11/2015 02:04 PM, Ali Çehreli wrote:The same keyword has a different use with templates:And the official documentation: http://dlang.org/template.html#TemplateAliasParameter Ali
Sep 11 2015
On Friday, 11 September 2015 at 21:06:32 UTC, Ali Çehreli wrote:On 09/11/2015 02:04 PM, Ali Çehreli wrote:Thanks again!The same keyword has a different use with templates:And the official documentation: http://dlang.org/template.html#TemplateAliasParameter
Sep 12 2015