Реализация Groovy на curry
самом деле не карри в любой момент, даже за кулисами. Это практически идентично частичному применению.
В curry
, rcurry
и ncurry
методы возвращают CurriedClosure
объект , который удерживает связанные аргументы. У него также есть метод getUncurriedArguments
(неправильно названный - функции карри, а не аргументы), который возвращает композицию аргументов, переданных ему со связанными аргументами.
Когда укупорочное вызывается, в конечном итоге это вызывает в invokeMethod
методMetaClassImpl
, который явно проверяет , чтобы увидеть , если вызывающий объект является экземпляром CurriedClosure
. Если это так, он использует вышеупомянутое, getUncurriedArguments
чтобы составить полный массив аргументов для применения:
if (objectClass == CurriedClosure.class) {
// ...
final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
// [Ed: Yes, you read that right, curried = uncurried. :) ]
// ...
return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}
Основываясь на запутанной и несколько непоследовательной номенклатуре, приведенной выше, я подозреваю, что тот, кто написал это, имел хорошее концептуальное понимание, но, возможно, был немного поспешным и - как и многие умные люди - смешал карри с частичным применением. Это понятно (см. Ответ Пола Кинга), если немного неудачно; будет трудно исправить это без нарушения обратной совместимости.
Одно из предложенных мною решений - перегрузить curry
метод так, чтобы при отсутствии аргументов он выполнял реальное каррирование, и не рекомендуется вызывать метод с аргументами в пользу новой partial
функции. Это может показаться немного странным , но это максимизирует обратную совместимость - поскольку нет причин использовать частичное приложение с нулевыми аргументами - при этом избегая (IMHO) более уродливой ситуации наличия новой функции с другим именем для правильного каррирования, в то время как функция фактически named curry
делает что-то другое и похожее до смешного.
Само собой разумеется, что результат вызова curry
полностью отличается от фактического карри. Если бы она действительно выполняла функцию, вы могли бы написать:
def add = { x, y -> x + y }
def addCurried = add.curry() // should work like { x -> { y -> x + y } }
def add1 = addCurried(1) // should work like { y -> 1 + y }
assert add1(1) == 2
... и это будет работать, потому что addCurried
должно работать как { x -> { y -> x + y } }
. Вместо этого он выдает исключение времени выполнения, и вы немного умираете внутри.