Каррирование и частичное применение

Я, оказывается, путал раньше каррирование и частичное применение. Даже не то чтобы путал, просто не пытался разобраться в чём разница между терминами, применяя то и другое по мере надобности. Недавно на «Хабре» в комментариях мне на моё незнание указали и я уяснил, наконец, что конкретно стоит за каждым термином.

Каррирование — преобразование функции от двух аргументов в функцию от первого аргумента, возвращающую функцию, результат вызова которой со вторым агрументом эквивалентен вызову первоначальной функции с упомянутыми аргументами. Немного запутанно, с примерами станет всё яснее. Я буду объяснять на примере Джаваскрипта, его больше народу знает, а на ПХП очень уж многословно получается. Кстати, в соответствующую статью в «Википедии» я добавил пример на «Гугл Гоу».
// функция, суммирующая два числа
function sum(x, y) {
    return x+y;
}

alert(sum(2, 8)); // вернёт 10

function sum(x) {
    return function (y) {
        return x+y;
    }
}

alert(sum(2)(8)); // тоже вернёт 10
При вызове второй функции sum создаётся функция, в которую из-за замыкания, «железно» попадает аргумент «x» со значением «2». Поэтому возвращаемая функция будет всегда суммировать с двойкой. С этим фактом тесно связано частичное применение, потому что оно только что, фактически, состоялось.

Частичное применение функции (Partial function) — создание некой функции на базе указанной, где некоторые агрументы заменены конкретными значениями. Например:
function partial_one_arg(f, a) {
    return function(b) {
        return f(a, b);
    }
}

min0 = partial_one_arg(Math.min, 0);
min1 = partial_one_arg(Math.min, 1);

alert([min0(-5), min0(5)]); // вернёт [-5, 0]
alert([min1(-5), min1(5)]); // вернёт [-5, 1]
В примере я заменил один из агрументов фунции нахождения минимального значения на конкретное значение, теперь при вызове функции «min0» сравнение будет идти с нулём, при вызове «min1» — с единицей.

Очень простые штуки, часто встречающиеся в функциональном программировании.
6 марта 2012 19:38

Artemy Tregubenko (arty.name)
6 марта 2012, 20:25

alert([min0(-5), min0(5)]); // вернёт [-5, 0]
alert([min1(-5), min1(5)]); // вернёт [-5, 1]

по крайней мере, у меня в интерпретаторе сейчас так вышло

и разницу я так и не понял, возможно потому, что в одном примере сумма, а в другом Math.min, и в одном примере каррирование делается вручную, а во втором автоматизируется

bolk (bolknote.ru)
6 марта 2012, 20:55, ответ предназначен Artemy Tregubenko (arty.name):

Да, перепутал немного.
и разницу я так и не понял, возможно потому, что в одном примере сумма, а в другом Math.min, и в одном примере каррирование делается вручную, а во втором автоматизируется
Понятия близкие, я же говорю. В одном случае функция из двух аргументов переводится в две функции одного аргумента, во втором — создаётся функция на основе другой, но с заранее подставленными некоторыми параметрами.

ninjacolumbo (ninjacolumbo.ya.ru)
9 марта 2012, 16:18

В питоне с версии 2.6 в functools есть функция partial, реализующая частичное применение. С её использованием есть небольшие тонкости. Дело в том, что она возвращает не функцию, а partial object, который похож на функцию. Помимо того, что у такого объекта нет __name__ по умолчанию (можно выставить руками), функция isfunction из модуля inspect (которая для проверки, в свою очередь, использует FunctionType из модуля types) для partial-объекта возвращает False. Отсюда вытекают всякие забавные эффекты, например, partial-функцию нельзя зарегистрировать в качестве фильтра в Django-шаблонах.

bolk (bolknote.ru)
9 марта 2012, 18:44, ответ предназначен ninjacolumbo (ninjacolumbo.ya.ru):

Спасибо, я таких подробностей про неё не знал. Интересно, для чего так сделано?

bolk (bolknote.ru)
9 марта 2012, 19:12, ответ предназначен ninjacolumbo (ninjacolumbo.ya.ru):

Можно сделать quickfix:
import functools
def a(arg1, arg2):
        return [arg1, arg2]
b = lambda *args, **kargs: functools.partial(a, 1)(*args, **kargs)
print type(b), b.__name__, b(2)
но всё равно ерунда получается (плохой __name__, нет __doc__), проще partial переписать полностью.

ninjacolumbo (ninjacolumbo.ya.ru)
10 марта 2012, 12:30, ответ предназначен bolk (bolknote.ru):

Сложно сказать для чего так сделано, partial написан на си — я не стал туда лезть. Но, как говорит Спольски, в данном случае абстракция дала течь %) Callable-объект не может быть использован в качестве функции, причём однозначно сказать, кто виноват: functools, inspect или Django — не получится.

bolk (bolknote.ru)
10 марта 2012, 18:48, ответ предназначен ninjacolumbo (ninjacolumbo.ya.ru):

Ну, как учат нас утки, не надо завязываться на то как объект называется, надо смотреть на то, что он умеет. С этой точки зрения, Джанга не права, вероятно.

bolk (bolknote.ru)
10 марта 2012, 19:59, ответ предназначен ninjacolumbo (ninjacolumbo.ya.ru):

Смотри:
In [1]: import functools
In [2]: s = functools.partial(sum, [1])
In [3]: callable(s)
Out[3]: True

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)