www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How To: Passing curried functions around

reply "Bahman Movaqar" <b.movaqar gmail.com> writes:
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
next sibling parent reply "welkam" <wwwelkam gmail.com> writes:
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
parent reply "Bahman Movaqar" <b.movaqar gmail.com> writes:
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
parent "welkam" <wwwelkam gmail.com> writes:
Now its clearer to me. You want delegates
http://wiki.dlang.org/Function_literals
Sep 06 2015
prev sibling next sibling parent reply "Meta" <jared771 gmail.com> writes:
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
parent "Bahman Movaqar" <b.movaqar gmail.com> writes:
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
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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 correct
That 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
parent reply "Bahman Movaqar" <b.movaqar gmail.com> writes:
On Friday, 11 September 2015 at 06:14:18 UTC, Ali Çehreli wrote:
 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);
Great. This is easily read by my eyes.
 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.
 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.
That's correct. I realised this subtle point later on, but had already switched to a `struct`y approach.
 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
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/11/2015 02:41 AM, Bahman Movaqar wrote:
 On Friday, 11 September 2015 at 06:14:18 UTC, Ali Çehreli wrote:
 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.
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.)
 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.
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. (?)
   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) {
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.
     while (true) {
         int i;
         write(prompt);
         readf(" %s", &i);
         if (validator(i)) {
             return i;
         } else {
             writeln("Sorry, that's not acceptable");
         }
     }
 }
The should be obvious.
 void foo(alias reader)() {
     reader();
 }
Another function template that takes a callable entity and calls it.
 void main() {
     auto reader = () => readValidInt!isEven("Enter an integer: ");
The syntax above creates a lambda with this definition: Call readValidInt!isEven with the string argument "Enter an integer: ".
     foo!reader();
foo takes that lambda. When foo eventually calls the lambda, readValidInt!isEven("Enter an integer: ") will be called.
 }

 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.
Ali
Sep 11 2015
parent reply "Bahman Movaqar" <b.movaqar gmail.com> writes:
On Friday, 11 September 2015 at 18:39:15 UTC, Ali Çehreli wrote:
 import std.stdio;

 bool isEven(int n) {
     return !(n % 2);
 }

 int readValidInt(alias validator)(string prompt) {
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.
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`?
Sep 11 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/11/2015 01:07 PM, Bahman Movaqar wrote:
 On Friday, 11 September 2015 at 18:39:15 UTC, Ali Çehreli wrote:
 import std.stdio;

 bool isEven(int n) {
     return !(n % 2);
 }

 int readValidInt(alias validator)(string prompt) {
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.
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`?
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 Ali
Sep 11 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
parent "Bahman Movaqar" <b.movaqar gmail.com> writes:
On Friday, 11 September 2015 at 21:06:32 UTC, Ali Çehreli wrote:
 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
Thanks again!
Sep 12 2015