How To: Passing curried functions around

Ali Çehreli via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Thu Sep 10 23:14:17 PDT 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 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



More information about the Digitalmars-d-learn mailing list