I read This article and i found it interesting.
To sum it up for those who don't want to read the entire post. The author implements a higher order function named Curry like this (refactored by me without his internal class):
public static Func<T1, Func<T2, TResult>>
Curry<T1, T2, TResult>(this Func<T1, T2, TResult> fn)
{
Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curry =
f => x => y => f(x, y);
return curry(fn);
}
That gives us the ability to take an expression like F(x, y) eg.
Func<int, int, int> add = (x, y) => x + y;
and call it in the F.Curry()(x)(y) manner;
This part i understood and i find it cool in a geeky way. What i fail to wrap my head around is the practical usecases for this approach. When and where this technique is necessary and what can be gained from it?
Thanks in advance.
Edited: After the initial 3 responses i understand that the gain would be that in some cases when we create a new function from the curried some parameters are not re evalued. I made this little test in C# (keep in mind that i'm only interested in the C# implementation and not the curry theory in general):
public static void Main(string[] args)
{
Func<Int, Int, string> concat = (a, b) => a.ToString() + b.ToString();
Func<Int, Func<Int, string>> concatCurry = concat.Curry();
Func<Int, string> curryConcatWith100 = (a) => concatCurry(100)(a);
Console.WriteLine(curryConcatWith100(509));
Console.WriteLine(curryConcatWith100(609));
}
public struct Int
{
public int Value {get; set;}
public override string ToString()
{
return Value.ToString();
}
public static implicit operator Int(int value)
{
return new Int { Value = value };
}
}
On the 2 consecutive calls to curryConcatWith100 the ToString() evaluation for the value 100 is called twice (once for each call) so i dont see any gain in evaluation here. Am i missing something ?
Currying is used to transform a function with x parameters to a function with y parameters, so it can be passed to another function that needs a function with y parameters.
For example, Enumerable.Select(this IEnumerable<T> source, Func<TSource, bool> selector)
takes a function with 1 parameter. Math.Round(double, int)
is a function that has 2 parameters.
You could use currying to "store" the Round
function as data, and then pass that curried function to the Select
like so
Func<double, int, double> roundFunc = (n, p) => Math.Round(n, p);
Func<double, double> roundToTwoPlaces = roundFunc.Curry()(2);
var roundedResults = numberList.Select(roundToTwoPlaces);
The problem here is that there's also anonymous delegates, which make currying redundant. In fact anonymous delegates are a form of currying.
Func<double, double> roundToTwoPlaces = n => Math.Round(n, 2);
var roundedResults = numberList.Select(roundToTwoPlaces);
Or even just
var roundedResults = numberList.Select(n => Math.Round(n, 2));
Currying was a way of solving a particular problem given the syntax of certain functional languages. With anonymous delegates and the lambda operator the syntax in .NET is alot simpler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With