F# curried function

前端 未结 6 2376
独厮守ぢ
独厮守ぢ 2020-12-15 06:02

Anyone have a decent example, preferably practical/useful, they could post demonstrating the concept?

6条回答
  •  春和景丽
    2020-12-15 06:27

    Currying describes the process of transforming a function with multiple arguments into a chain of single-argument functions. Example in C#, for a three-argument function:

    Func>> Curry(Func f)
    {
        return a => b => c => f(a, b, c);
    }
    
    void UseACurriedFunction()
    {
        var curryCompare = Curry(String.Compare);
        var a = "SomeString";
        var b = "SOMESTRING";
        Console.WriteLine(String.Compare(a, b, true));
        Console.WriteLine(curryCompare(a)(b)(true));
    
        //partial application
        var compareAWithB = curryCompare(a)(b);
        Console.WriteLine(compareAWithB(true));
        Console.WriteLine(compareAWithB(false));
    }
    

    Now, the boolean argument is probably not the argument you'd most likely want to leave open with a partial application. This is one reason why the order of arguments in F# functions can seem a little odd at first. Let's define a different C# curry function:

    Func>> BackwardsCurry(Func f)
    {
        return a => b => c => f(c, b, a);
    }
    

    Now, we can do something a little more useful:

    void UseADifferentlyCurriedFunction()
    {
        var curryCompare = BackwardsCurry(String.Compare);
    
        var caseSensitiveCompare = curryCompare(false);
        var caseInsensitiveCompare = curryCompare(true);
    
        var format = Curry(String.Format)("Results of comparing {0} with {1}:");
    
        var strings = new[] {"Hello", "HELLO", "Greetings", "GREETINGS"};
    
        foreach (var s in strings)
        {
            var caseSensitiveCompareWithS = caseSensitiveCompare(s);
            var caseInsensitiveCompareWithS = caseInsensitiveCompare(s);
            var formatWithS = format(s);
    
            foreach (var t in strings)
            {
                Console.WriteLine(formatWithS(t));
                Console.WriteLine(caseSensitiveCompareWithS(t));
                Console.WriteLine(caseInsensitiveCompareWithS(t));
            }
        }
    }
    

    Why are these examples in C#? Because in F#, function declarations are curried by default. You don't usually need to curry functions; they're already curried. The major exception to this is framework methods and other overloaded functions, which take a tuple containing their multiple arguments. You therefore might want to curry such functions, and, in fact, I came upon this question when I was looking for a library function that would do this. I suppose it is missing (if indeed it is) because it's pretty trivial to implement:

    let curry f a b c = f(a, b, c)
    
    //overload resolution failure: there are two overloads with three arguments.
    //let curryCompare = curry String.Compare
    
    //This one might be more useful; it works because there's only one 3-argument overload
    let backCurry f a b c = f(c, b, a)
    let intParse = backCurry Int32.Parse
    let intParseCurrentCultureAnyStyle = intParse CultureInfo.CurrentCulture NumberStyles.Any
    let myInt = intParseCurrentCultureAnyStyle "23"
    let myOtherInt = intParseCurrentCultureAnyStyle "42"
    

    To get around the failure with String.Compare, since as far as I can tell there's no way to specify which 3-argument overload to pick, you can use a non-general solution:

    let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b)
    let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)
    

    I won't go into detail about the uses of partial function application in F# because the other answers have covered that already.

提交回复
热议问题