В Groovy есть концепция, которая называется «карри». Вот пример из их вики:
def divide = { a, b -> a / b }
def halver = divide.rcurry(2)
assert halver(8) == 4
Мое понимание того, что здесь происходит, заключается в том, что правый аргумент divide
связывается со значением 2. Это похоже на форму частичного применения.
Термин карринг обычно используется для обозначения преобразования функции, которая принимает серию аргументов, в функцию, которая принимает только один аргумент и возвращает другую функцию. Например, вот тип curry
функции в Haskell:
curry :: ((a, b) -> c) -> (a -> (b -> c))
Для людей, которые не использовали Haskell a
, b
и c
имеют все общие параметры. curry
принимает функцию с двумя аргументами и возвращает функцию, которая принимает a
и возвращает функцию из b
в c
. Я добавил пару дополнительных скобок к типу, чтобы сделать это более понятным.
Я неправильно понял, что происходит в отличном примере, или это просто неправильное частичное применение? Или это действительно делает и то и другое: то есть преобразовать divide
в карри функцию и затем частично применить 2
к этой новой функции.
источник
Ответы:
Реализация Groovy на
curry
самом деле не карри в любой момент, даже за кулисами. Это практически идентично частичному применению.В
curry
,rcurry
иncurry
методы возвращаютCurriedClosure
объект , который удерживает связанные аргументы. У него также есть методgetUncurriedArguments
(неправильно названный - функции карри, а не аргументы), который возвращает композицию аргументов, переданных ему со связанными аргументами.Когда укупорочное вызывается, в конечном итоге это вызывает в
invokeMethod
методMetaClassImpl
, который явно проверяет , чтобы увидеть , если вызывающий объект является экземпляромCurriedClosure
. Если это так, он использует вышеупомянутое,getUncurriedArguments
чтобы составить полный массив аргументов для применения:Основываясь на запутанной и несколько непоследовательной номенклатуре, приведенной выше, я подозреваю, что тот, кто написал это, имел хорошее концептуальное понимание, но, возможно, был немного поспешным и - как и многие умные люди - смешал карри с частичным применением. Это понятно (см. Ответ Пола Кинга), если немного неудачно; будет трудно исправить это без нарушения обратной совместимости.
Одно из предложенных мною решений - перегрузить
curry
метод так, чтобы при отсутствии аргументов он выполнял реальное каррирование, и не рекомендуется вызывать метод с аргументами в пользу новойpartial
функции. Это может показаться немного странным , но это максимизирует обратную совместимость - поскольку нет причин использовать частичное приложение с нулевыми аргументами - при этом избегая (IMHO) более уродливой ситуации наличия новой функции с другим именем для правильного каррирования, в то время как функция фактически namedcurry
делает что-то другое и похожее до смешного.Само собой разумеется, что результат вызова
curry
полностью отличается от фактического карри. Если бы она действительно выполняла функцию, вы могли бы написать:... и это будет работать, потому что
addCurried
должно работать как{ x -> { y -> x + y } }
. Вместо этого он выдает исключение времени выполнения, и вы немного умираете внутри.источник
and you die a little inside
Я думаю, что ясно, что Groovy Curry на самом деле является частичным применением при рассмотрении функций с более чем двумя аргументами. рассмотреть возможность
его карри форма будет
однако карри groovy вернет что-то эквивалентное (при условии, что вызывается с 1 аргументом x)
который будет вызывать f со значением фиксированного х
т. е. хотя groovy-карри может возвращать функции с N-1 аргументами, у функций с карри только один аргумент, поэтому groovy не может быть карри с карри
источник
Groovy заимствовал наименование своих методов карри из множества других не чистых языков FP, которые также используют аналогичное именование для частичного применения - возможно, неудачно для такой функциональности, ориентированной на FP. Существует несколько «реальных» реализаций каррирования, предлагаемых для включения в Groovy. Хорошая тема, чтобы начать читать о них здесь:
http://groovy.markmail.org/thread/c4ycxdzm3ack6xxb
Существующая функциональность останется в той или иной форме, и обратная совместимость будет приниматься во внимание при вызове того, как назвать новые методы и т. Д., Поэтому я не могу сказать на данном этапе, каким будет окончательное именование новых / старых методов. быть. Наверное, компромисс по именованию, но посмотрим.
Для большинства программистов ОО различие между двумя терминами (карринг и частичное применение), возможно, в значительной степени академическое; однако, как только вы привыкнете к ним (и тот, кто будет поддерживать ваш код, будет обучен чтению этого стиля кодирования), тогда бессмысленное программирование или программирование в неявном стиле (которое поддерживает «реальное» каррирование) позволяет более компактно выражать определенные виды алгоритмов. а в некоторых случаях более элегантно. Очевидно, здесь есть некоторая «красота в глазах смотрящего», но способность поддерживать оба стиля соответствует природе Groovy (OO / FP, static / dynamic, классы / сценарии и т. Д.).
источник
Учитывая это определение, найденное в IBM:
halver
ваша новая (карри) функция (или замыкание), которая теперь принимает только один параметр. Звонокhalver(10)
приведет к 5.Для этого он преобразует функцию с n аргументами в функцию с n-1 аргументами. То же самое говорит ваш пример на Haskell, что делает карри.
источник
rcurry
функцией (которая принимает один аргумент) в функцию (с теперь только одним аргументом). Я приковал функцию curry аргументом к моей базовой функции, чтобы получить свою результирующую функцию.n - 1
аргументами. Смотрите пример в конце моего ответа; также см. далее в статье для получения дополнительной информации о проводимых различий. en.wikipedia.org/wiki/…partial
.