Каковы реальные случаи каррирования?

Я читал много статей о currying, но почти все они вводят в заблуждение, объясняя currying как приложение с частичной функцией, и все примеры обычно относятся к функциям с arity of 2, например функцией add или чем-то.

Также многие реализации функции curry в JavaScript заставляют принимать более 1 аргумента для частичного приложения (см. lodash), когда Статья в Википедии ясно говорит о том, что каррирование:

перевод оценки функции, которая принимает несколько аргументов (или кортеж аргументов) в оценку последовательности функций, каждая с одним аргументом (частичное приложение)

Таким образом, в основном currying представляет собой серию частичных приложений, каждая из которых имеет один аргумент. И я действительно хочу знать реальное использование этого, на любом языке.

Ответ 1

Реальный случай каррирования - это частичное приложение.

Каррирование само по себе не очень интересно. Что интересно, если ваш язык программирования поддерживает каррирование по умолчанию, как в случае с F # или Haskell.

Вы можете определить функции более высокого порядка для currying и частичного приложения на любом языке, который поддерживает функции первого класса, но это далеко от гибкости, которую вы получаете, когда каждая функция, которую вы получаете, берется и, таким образом, частично применима, что-нибудь.

Итак, если вы видите людей, объединяющих currying и частичное приложение, то из-за того, насколько тесно эти понятия связаны между собой - поскольку каррирование вездесущее, вам действительно не нужны другие формы частичного приложения, чем применение курсовых функций к последовательным аргументам.

Ответ 2

Полезно передать контекст.

Рассмотрим функцию "map". Он принимает функцию как аргумент:

map : (a -> b) -> [a] -> [b]

Для функции, использующей какой-либо вид контекста:

f    : SomeContext -> a -> b

Это означает, что вы можете элегантно использовать функцию карты, не указывая "a" -аргумент:

map (f actualContext) [1,2,3]

Без currying вам придется использовать лямбда:

map (\a -> f actualContext a) [1,2,3]

Примечания:

map - это функция, которая берет список, содержащий значения a, функцию f. Он создает новый список, беря каждый a и применяя к нему f, в результате чего появляется список b

например. map (+1) [1,2,3] = [2,3,4]

Ответ 3

Каркас подшипника имеет код, который можно разделить на два набора проблем (я использую Haskell для иллюстрации). Синтаксический, Реализация.

Синтаксис Проблема 1:

Currying позволяет повысить четкость кода в некоторых случаях. Что означает ясность? Чтение функции обеспечивает четкую индикацию ее функциональности. например Функция отображения.

map : (a -> b) -> ([a] -> [b])

Прочитайте таким образом, мы видим, что карта является функцией более высокого порядка, которая поднимает функцию, преобразующую as в bs в функцию, которая преобразует [a] в [b].

Эта интуиция особенно полезна при понимании таких выражений.

map (map (+1))

Внутреннее отображение имеет тип выше [a] -> [b]. Чтобы выяснить тип внешней карты, мы рекурсивно применяем нашу интуицию сверху. Таким образом, внешняя карта поднимает [a] -> [b] до [[a]] -> [[b]].

Эта интуиция перенесет вас вперед. Как только мы обобщим map на fmap, a map на произвольных контейнерах, становится очень легко читать такие выражения (Примечание. Я мономорфозировал тип каждого fmap для другого типа ради пример).

showInt : Int -> String
(fmap . fmap . fmap) showInt : Tree (Set [Int]) -> Tree (Set [String])

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

Синтаксис Проблема 2:

Currying также позволяет выразить функции в бессточной форме.

nthSmallest : Int -> [Int] -> Maybe Int
nthSmallest n = safeHead . drop n . sort

safeHead (x:_) = Just x
safeHead _     = Nothing

Выше, как правило, считается хорошим стилем, поскольку он иллюстрирует мышление в терминах конвейера функций, а не явное манипулирование данными.

Реализация

В Haskell бессмысленный стиль (через currying) может помочь нам оптимизировать функции. Запись функции в свободной форме точки позволит нам ее memoize.

memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!)
    where fib 0 = 0
          fib 1 = 1
          fib n = memoized_fib (n-2) + memoized_fib (n-1)


not_memoized_fib  :: Int -> Integer
not_memoized_fib x = map fib [0 ..] !! x
    where fib 0 = 0
          fib 1 = 1
          fib n = not_memoized_fib (n-2) + not_memoized_fib (n-1)

Запись в виде карри-функции, как в memoized-версии, рассматривает функцию curried как сущность и поэтому запоминает ее.