18

Given this simple currying function:

func foo(x:Int)(y:Int)->String{
  return "\(x) with \(y)"
}

I'd expect to be able to do something like this:

let bar = foo(1)
bar(2) //<- error: Missing argument label 'y:' in call

If I label the call to bar (as in bar(y:2)) everything works fine. But I don't understand why the parameter name is necessary. Is there any way to avoid it?

The obvious thing:

func foo(x:Int)(_ y:Int)->String ...

does not seem to work.

jemmons
  • 18,605
  • 8
  • 55
  • 84
  • 3
    Nice! The issue exists both for a top-level func and for a class method. – GoZoner Jun 11 '14 at 19:35
  • Yeah; even the example of currying in the Swift book shows this up (paste it in, delete the second "manual" currying function, and you get "missing argument label 'b:' in call") – Matt Gibson Jun 11 '14 at 20:02
  • Wrote this up as rdar://17359591. Please feel free to dupe. – jemmons Jun 18 '14 at 14:00

5 Answers5

8

It's a bug, you should file a radar at bugreport.apple.com

As a confirmation, if you place an underscore, like this

func foo(x: Int)(_ y: Int) -> String

you get a warning

Extraneous '_' in parameter: 'y' has no keyword argument name

So it explicitly says that y has no external name, but it still requires one when called, which is clearly against the language specification.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
1

I believe it is a compiler bug, your example should work as described in The Swift Programming Language book where they mention declaring curried functions:

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

addTwoNumbers(4)(5) // Returns 9

https://bugreport.apple.com

good find!

fqdn
  • 2,823
  • 1
  • 13
  • 16
0

I am not sure I fully understand your currying. Here is my take on it. I have a function foo as follows:

func foo(x:Int, y:Int) -> String{
  return "\(x) with \(y)"
}

let bar = foo(1, 2) // gives "1 with 2"

I wish to curry this function to 'fix' the value for x, so do so as follows:

func fooCurry(x:Int) -> (Int -> String) {
  func curry(y:Int) -> String {
    return foo(x, y)
  }
  return curry
}

The above returns a new function which can be used as follows:

let curriedFoo = fooCurry(1)
let barWithCurry = curriedFoo(2) // gives "1 with 2"

The function returned by fooCurry has the signature (Int -> String), which means that the parameter does not have an external name.

ColinE
  • 68,894
  • 15
  • 164
  • 232
  • 3
    what you're doing here is basically currying 'by hand'. `func foo(x: Int)(y: Int) -> String` should work out of the box as a language feature, and it doesn't. – Gabriele Petronella Jun 11 '14 at 19:52
0

Not the best syntax, but if you want to get around it for now, you can use the following for basic curried functions:

func foo(x:Int) -> Int -> String {
  return {
    return "\(x) with \($0)"
  }
}

Then you can just do:

let bar = foo(1)
bar(2) //-> 1 with 2

Now obviously the problem with this becomes obvious when you want to write a curried function for piping four Ints for example:

func makerAdders(a:Int)(b:Int)(c:Int)(d:Int) {...}

becomes like this:

func add(a:Int) -> Int -> Int -> Int -> Int {
  return { 
    b in return {
      c in return {
        d in return a + b + c + d 
      }
    }
  }
}

The inner closures make it a bit better than using inner functions, but again it defeats the purpose of the nice func add(a:Int)(b:Int)(c:Int)(d:Int) {return a+b+c+d} syntax.

AJ Meyghani
  • 4,389
  • 1
  • 31
  • 35
0

Definitely a bug in the compiler as far as I can tell. Until it's fixed you can get a properly curried version of any function using these functions (note that I've included cases for two and three arguments, extend at your leisure:

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

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

Just use:

curry(addTwoNumbers)(1)(2)
David Berry
  • 40,941
  • 12
  • 84
  • 95