Curry Function in Swift

梦想与她 提交于 2019-11-27 14:49:16

You can achieve this pretty easily with closures:

/// Takes a binary function and returns a curried version
func curry<A,B,C>(f: (A, B) -> C) -> A -> B -> C {
    return { a in { b in f(a, b) } }
}

curry(+)(5)(6) // => 11

let add: Int -> Int -> Int = curry(+)
add(5)(6) // => 11

It would be really nice to be able to do the same thing for functions that take 3, 4 or more arguments, but without duplicating the implementation. The signature of such a function might start something like:

/// Take a function accepting N arguments and return a curried version
func curry<T>(args: T...) -> /* ? */

What would the return type be? It would change based on the input to the function. This definitely isn't possible in Swift at the moment, and I don't think it would be possible at all without some kind of macro system. But even with macros I don't think the compiler would be satisfied unless it knew the length of the list at compile-time.

Having said that, it's really straight-forward to manually overload the currying function with a version that accepts 3, 4, 5 or more parameters:

func curry<A,B,C,D>(f: (A, B, C) -> D) -> A -> B -> C -> D {
    return { a in { b in { c in f(a,b,c) } } }
}

func curry<A,B,C,D,E>(f: (A, B, C, D) -> E) -> A -> B -> C -> D -> E {
    return { a in { b in { c in { d in f(a,b,c,d) } } } }
}

// etc.

I'm not sure this is actually going to be possible in the same way it is inside of languages like Python.

The core problem I see to having a single generic solution is the strong typing of the closures/funcs you want to accept.

You could fairly easily create a curry function that worked on a specific or common function signature, but as far as a general purpose curry I don't see a way for it to work. The issue is more than about the types of the arguments (as mentioned in comments) but also with the number of them.

I've written up a simple example of how you could implement a curry function. It works, but I don't see a sane way to have a truly generic one like you can in more loosely typed languages.

func add(a1: Int, a2: Int) -> Int {
    return a1 + a2
}

func curry(argument: Int, block: (Int, Int) -> Int) -> Int -> Int{
    func curried(arg: Int) -> Int {
        return block(argument, arg)
    }

    return curried
}

curry(5, add)(6)

In case you want to quickly get the curry function for any number of parameters, it's possible to generate it as shown in this gist.

The code is in Swift 2.2 and generates code for Swift 2.2 (at the moment). It uses simple template-based approach (a possible alternative is constructing an AST followed by code-generation):

func genCurry(n: Int, indent: Indent = .fourSpaces, accessLevel: AccessLevel = .Default, verbose: Bool = false) -> String {

    // ...
    // The bulky park is skipped for clarity.

    return accessLevel.asPrefix + "func curry<\(genericParams)>(f: \(fSig)) -> \(curriedSig(n)) {\n"
        + indent.single + "return \(closure)\n"
        + "}\n"
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!