11

I want to make a function that return a curry function like below

func addTwoNumbers(a: Int)(b: Int) -> Int {
    return a + b
}

addTwoNumbers(4)(b: 6) // Result: 10

var add4 = addTwoNumbers(4)
add4(b: 10) // returns 14     

What is the return type of such function and how can I generate a function like this using a function that take Variadic parameters.

func generateCurry(.../*Variadic parameters*/) -> .../*curry function type*/ {
  return ...//curry function
}

I want a generic solution and not take only Int as arguments in the parmeter of the generateCurry function

let curried = curry(func(a, b, c) {
  print(a + b + c)
})
curried(1)(2)(3) //prints 6
Gavin
  • 8,204
  • 3
  • 32
  • 42
Encore PTL
  • 8,084
  • 10
  • 43
  • 78
  • Hi, so what you want to do is: a function that takes another function as parameter and within that function being able to infer which function to call based on type? In addition, your `curried` now prints 6, but what would you like to do when it is of type let say `String`? – Unheilig Jun 08 '14 at 16:10
  • You can have any type but the point is to evaluate the block of code passed in the curry function only after all the arguments have been passed. I have updated the question to show an example using addTwoNumbers method – Encore PTL Jun 08 '14 at 16:14
  • I find this article http://www.russbishop.net/swift-function-currying to be extremely useful and well explained – onmyway133 Oct 13 '14 at 07:16

4 Answers4

15

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.
Adam Sharp
  • 3,618
  • 25
  • 29
1

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)
Bryan McLemore
  • 6,438
  • 1
  • 26
  • 30
  • We can use generics and make sure all arguments passed are of type T – Encore PTL Jun 08 '14 at 14:49
  • 1
    You still run into the fact that a function with different numbers of arguments is a different signature, and thus a different type. The varadic requirement is part of the problem. – Bryan McLemore Jun 08 '14 at 14:52
  • If we can solve the issue of passing an array in a Variadic function then we can solve this issue. Is there a way in the language to call Variadic method like performSelector in Objective C or can we bridge to Objective C to make it work? – Encore PTL Jun 08 '14 at 15:59
  • In swift the variadic implementation appears to effectively be an array. I did just try to implement a version of my example that uses Any to some effect. I was able to crash Xcode using it and got a truly impressive compiler error by doing `var test: Any = curry(5,add)`. I'm keep getting back to a generic curry being impossible in swift, or minimally way more effort than it's worth. – Bryan McLemore Jun 08 '14 at 17:52
0

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"
}
werediver
  • 4,667
  • 1
  • 29
  • 49
0

I recently found that currying was removed back in Swift3. I created my own version which is repetitive but does the job.

precedencegroup CurryPrecedence {
    associativity: left
    higherThan: MultiplicationPrecedence
}
infix operator <<== :CurryPrecedence
//1 param
func <<==<A,Z>(_ f: @escaping (A) -> (Z), _ p:A) -> () -> (Z)  {
    { f(p) }
}
//2 param
func <<==<A,B,Z>(_ f: @escaping (A, B) -> (Z), _ p:B) -> (A) -> (Z)  {
    { (A) in f(A,p) }
}
//3 param
func <<==<A,B,C,Z>(_ f: @escaping (A, B, C) -> (Z), _ p:C) -> (A, B) -> (Z)  {
    { (A, B) in f(A,B,p) }
}
//4 param
func <<==<A,B,C,D,Z>(_ f: @escaping (A, B, C, D) -> (Z), _ p:D) -> (A, B, C) -> (Z)  {
    { (A, B, C) in f(A,B,C,p) }
}

To use it:

let ten = (addTwoNumbers <<== 6 <<== 4)()

or

let ten = (addTwoNumbers <<== 6)(4)
Mike Neilens
  • 91
  • 2
  • 2