tags:

views:

1251

answers:

5

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 ?

+3  A: 

Its easier to first consider fn(x,y,z). This could by curried using fn(x,y) giving you a function that only takes one parameter, the z. Whatever needs to be done with x and y alone can be done and stored by a closure that the returned function holds on to.

Now you call the returned function several times with various values for z without having to recompute the part the required x and y.

Edit:

There are effectively two reasons to curry.


Parameter reduction

As Cameron says to convert a function that takes say 2 parameters into a function that only takes 1. The result of calling this curried function with a parameter is the same as calling the original with the 2 parameters.

With Lambdas present in C# this has limited value since these can provide this effect anyway. Although it you are use C# 2 then the Curry function in your question has much greater value.

Staging computation

The other reason to curry is as I stated earlier. To allow complex/expensive operations to be staged and re-used several times when the final parameter(s) are supplied to the curried function.

This type of currying isn't truely possible in C#, it really takes a functional language that can natively curry any of its functions to acheive.


Conclusion

Parameter reduction via the Curry you mention is useful in C# 2 but is considerably de-valued in C# 3 due to Lambdas.

AnthonyWJones
Staging: Couldn't you create "curried" functions using Lambdas and then re-use them. Since lambdas are a form of currying you can do everything that currying can with them already.
Cameron MacFarland
Also I'd agree that currying is more useful in C#2 than in C#3, if it wasn't for the fact that the curry functions mentioned in the question are implemented using C#3 features. *shrug*
Cameron MacFarland
@Cameron: To your first comment: The problem is to reuse data that is computed once that data would have to be stored outside the lambda using a closure. By doing that though we will lose the semantics of that type of currying.
AnthonyWJones
A: 

In a sense, curring is a technique to enable automatic partial application.

More formally, currying is a technique to turn a function into a function that accepts one and only one argument.

In turn, when called, that function returns another function that accepts one and only one argument . . . and so on until the 'original' function is able to be executed.

from a thread in codingforums

I particularly like the explanation and length at which this is explained on this page.

Ric Tokyo
I got that part. The real life usability eludes me.
AZ
A: 

One example: You have a function compare(criteria1, criteria2, option1, option2, left, right). But when you want to supply the function compare to some method with sorts a list, then compare() must only take two arguments, compare(left, right). With curry you then bind the criteria arguments as you need it for sorting this list, and then finally this highly configurable function presents to the sort algorithm as any other plain compare(left,right).

Detail: .NET delegates employ implicit currying. Each non-static member function of a class has an implicit this reference, still, when you write delegates, you do not need to manually use some currying to bind this to the function. Instead C# cares for the syntactic sugar, automatically binds this, and returns a function which only requires the arguments left.

In C++ boost::bind et al. are used for the same. And as always, in C++ everything is a little bit more explicit (for instance, if you want to pass a instance-member function as a callback, you need to explicitly bind this).

gimpf
+7  A: 

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.

Cameron MacFarland
A: 

Hi, here's another example of how you might use a Curry function. Depending on some condition (e.g. day of week) you could decide what archive policy to apply before updating a file.

    void ArchiveAndUpdate(string[] files)
    {
        Func<string, bool> archiveCurry1 = (file) =>
            Archive1(file, "archiveDir", 30, 20000000, new[] { ".tmp", ".log" });

        Func<string, bool> archiveCurry2 = (file) =>
            Archive2("netoworkServer", "admin", "nimda", new FileInfo(file));

        Func<string, bool> archvieCurry3 = (file) => true;

        // backup locally before updating
        UpdateFiles(files, archiveCurry1);

        // OR backup to network before updating
        UpdateFiles(files, archiveCurry2);

        // OR do nothing before updating
        UpdateFiles(files, archvieCurry3);
    }

    void UpdateFiles(string[] files, Func<string, bool> archiveCurry)
    {
        foreach (var file in files)
        {
            if (archiveCurry(file))
            {
                // update file //
            }
        }
    }

    bool Archive1(string fileName, string archiveDir, 
        int maxAgeInDays, long maxSize, string[] excludedTypes)
    {
        // backup to local disk
        return true;
    }

    bool Archive2(string sereverName, string username, 
        string password, FileInfo fileToArchvie)
    {
        // backup to network
        return true;
    }
it depends