Proper Currying in C#

Proper Currying in C#

EDIT: As noted in comments, this is partial application rather than currying. I wrote a blog post on my understanding of the difference, which folks may find interesting.

Well, it's not particularly different - but I'd separate out the currying part from the "calling DoSomething" part:

public static Func<TResult> Apply<TResult, TArg> (Func<TArg, TResult> func, TArg arg)
{
return () => func(arg);
}

public static Func<TResult> Apply<TResult, TArg1, TArg2> (Func<TArg1, TArg2, TResult> func,
TArg1 arg1, TArg2 arg2)
{
return () => func(arg1, arg2);
}

// etc

Then:

DoSomething(Apply(foo, 1));

That way you can reuse the currying code in other situations - including cases where you don't want to call the newly-returned delegate immediately. (You might want to curry it more later on, for example.)

Currying is just optional parameters?

It has nothing to do with anything being optional.

But instead of defining a function which takes two parameters, you can define one which takes only one parameter, and returns a function which takes the other parameter.

The end result is the same (the caller ends up providing two parameters), but with currying, you only provide one at a time.

What is the difference between currying and partial application?

Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:

function f(x,y,z) { z(x(y));}

When curried, becomes:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

In order to get the full application of f(x,y,z), you need to do this:

f(x)(y)(z);

Many functional languages let you write f x y z. If you only call f x y or f(x)(y) then you get a partially-applied function—the return value is a closure of lambda(z){z(x(y))} with passed-in the values of x and y to f(x,y).

One way to use partial application is to define functions as partial applications of generalized functions, like fold:

function fold(combineFunction, accumulator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10

Are there alternate ways to input a parameter into a task besides using lambda functions?

Looking at the documentation for Task.Run, you'll notice that every definition takes either a Func<Task> or a Func<Result> or some combination therein. However, none of these definitions include a mention of parameters. Plus, what you're sending when you call Task.Run(DoSomethingElse(myInput)) is the result of calling DoSomethingElse(myInput) because this represents a call to the method itself rather than sending the method and its arguments as a parameter. In effect, that's what using a Lambda does. If you really don't want to insert a lambda into your code you could try adding a static method like this:

public static Task Run<TItem>(f Func<TItem, Task>, i TItem) {
return Task.Run(() => f(i));
}

Is there a way to cast Funcv1,v2,out1 to Funcout1 given I know v1 and v2?

Yes. Basically you just do

() => func(a, b)

How to pass an arbitrary function with some bound parameters to another function?

You can always redeclare Func yourself:

public delegate TReturn FFunc<TArg,TReturn>(TArg arg);

Which you can use thusly:

float pi = 3.14f;
CallLater((FFunc<int,string>)(delegate(int a) { return MyFunc(a, pi); }));

Partial function application in c# with changing bound variables

Crude, but just the seed for an idea:

Func<int,double,double> g;
g = (i,z) => (i==0) ? f(10,z) : (i==1) ? f(z,10) : etc

A more flexible and complicated suggestion:

Func<double[],double> f;
Func<int[],double[],double> g;
g = (i,d) => f(i.Select( x => d[x]).ToArray());

Somewhere between the two:

Func<int,double,double[],double> g;
g = (k,c,d) => f(d.Select( (i,x) => (i!=k) ? x : c).ToArray());


Related Topics



Leave a reply



Submit