Почему для карри-функций требуются имена внешних параметров?

Учитывая эту простую функцию каррирования:

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

Я ожидаю, что смогу сделать что-то вроде этого:

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

Если я отмечаю вызов bar (как в bar(y:2)), все работает нормально. Но я не понимаю, почему имя параметра необходимо. Есть ли способ избежать этого?

Очевидная вещь:

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

похоже, не работает.

Ответы

Ответ 1

Это ошибка, вы должны подать радар в bugreport.apple.com

В качестве подтверждения, если вы разместите символ подчеркивания, например

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

вы получите предупреждение

Посторонний '_' в параметре: 'y' не имеет имени аргумента ключевого слова

Поэтому он явно говорит, что y не имеет внешнего имени, но он все еще требует вызова при вызове, что явно противоречит спецификации языка.

Ответ 2

Я считаю, что это ошибка компилятора, ваш пример должен работать так, как описано в книге Swift Programming Language, где упоминается объявление карриных функций:

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

addTwoNumbers(4)(5) // Returns 9

https://bugreport.apple.com

хорошая находка!

Ответ 3

Я не уверен, что полностью понимаю твою карри. Вот мое занятие. У меня есть функция foo следующим образом:

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

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

Я хочу выполнить эту функцию, чтобы "исправить" значение для x, поэтому сделайте следующее:

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

Вышеприведенная функция возвращает новую функцию, которая может быть использована следующим образом:

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

Функция, возвращаемая fooCurry, имеет подпись (Int -> String), что означает, что параметр не имеет внешнего имени.

Ответ 4

Не лучший синтаксис, но если вы хотите обойти его сейчас, вы можете использовать следующие функции для основных карри:

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

Тогда вы можете просто сделать:

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

Теперь очевидно, что проблема с этим становится очевидной, если вы хотите написать функцию curried для трубопровода четыре Int, например:

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

будет выглядеть следующим образом:

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

Внутренние замыкания делают его немного лучше, чем использование внутренних функций, но снова он побеждает цель симпатичного синтаксиса func add(a:Int)(b:Int)(c:Int)(d:Int) {return a+b+c+d}.

Ответ 5

Определенно ошибка в компиляторе, насколько я могу судить. До тех пор, пока он не будет исправлен, вы можете получить правильную карриную версию любой функции, используя эти функции (обратите внимание, что я включил случаи для двух и трех аргументов, расширяя ваш досуг:

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) } } }
}

Просто используйте:

curry(addTwoNumbers)(1)(2)