Какова ценность карри в функциональном программировании? - PullRequest
5 голосов
/ 06 июля 2011

Я знаю концепцию и как использовать карри, но мне интересно, какова ее ценность на практике?

Ответы [ 3 ]

5 голосов
/ 07 июля 2011

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

  • улучшение повторного использования кода- функции специального случая - это просто частично примененные (и каррированные) универсальные функции
  • , улучшающие читабельность кода - map (+2) легче читать, чем map (\x -> x + 2)
  • улучшенная производительность - каррирование можетсделайте очевидными определенные специализации, и хороший компилятор создаст для вас специализированную версию
  • fun - более простой код, более красивый код делает жизнь более приятной.
2 голосов
/ 06 июля 2011

Реальные выгоды, которые я нашел:

  • Меньше ошибок - составление кода по составу функций приводит к более правильному коду, чем поток императивного управления. Например, если вы используете «карту», ​​а не «для циклов», вы устраняете риск многих ошибок индексации «выключено одним»

  • Улучшенный параллелизм - и код, который вы создаете с помощью чистых функций без побочных эффектов, автоматически поточно-ориентирован. Соедините это с неизменными постоянными структурами данных, и у вас есть отличный рецепт для написания надежного параллельного кода. Clojure особенно хорош для этого - см. http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

  • Более лаконичный и управляемый код - функциональный код в моем совершенно ненаучном анализе кажется значительно короче императивного ООП-кода. Преимущество имеет тенденцию быть более явным, поскольку программы становятся больше. Я думаю, что есть несколько причин для этого:

    • Синтаксис функциональных языков имеет тенденцию быть довольно лаконичным. Например, Haskell и различные Lisps имеют очень простой и элегантный синтаксис
    • Функциональные языки имеют тенденцию поощрять композицию (функций более высокого порядка), а не "склеивать" части программ вместе. Следовательно, вы избегаете множества шаблонов, которые присущи многим другим парадигмам.
    • Относительно точки композиции, я считаю, что проще применять принцип DRY в функциональных языках. Если вы видите общий шаблон, почти всегда можно довольно легко извлечь его в функцию более высокого порядка (или макрос, если вы Лиспер), чтобы вы могли использовать его в другом месте.
  • Тестируемость - когда вы пишете код в основном с использованием чистых функций, очень легко написать надежные тесты.

Конечно, есть некоторые недостатки, которые могут это исправить:

  • Труднее написать - вам нужно больше умственных способностей, потому что вам нужно держать в голове довольно сложные абстрактные понятия. Это полезно, если вы - обученный математик (я), но мне все еще кажется, что писать функциональный код сложнее, чем ООП.
  • Существует некоторое снижение производительности - в функциональных языках используются различные конструкции, которые обязательно предполагают некоторую степень загрузки. Хотя это может быть сделано довольно маленьким с хорошими компиляторами, это никогда не может быть полностью устранено.
  • Поддержка библиотек / инструментов - это почти полностью связано с большей зрелостью платформ и инструментов ООП, но тем не менее это проблема. В долгосрочной перспективе это не будет проблемой, но лучшее решение, которое я нашел, это использовать Clojure, который может извлечь выгоду из большинства библиотек и инструментов платформы Java.
1 голос
/ 06 июля 2011

Я бы сказал, что это немного похоже на Один и только один раз

В C / C ++ я нахожу себя пишущим код, такой как

configure_grid (grid, first_column, last_column, action) {
    for (i = first_column; i <= last_column; ++i)
        // ...
}

configure_grids (action) {
   congifure_grid (alpha, first_alpha, last_alpha, action);
   congifure_grid (beta, first_beta, last_beta, action);
}

Вместо написанияцикл for один раз для каждого из альфа и бета.Это аналогично карри в процедурном коде.Преимущество здесь очевидно.

Карринг является важной теоретической концепцией, но с точки зрения практических это преимущество.

На самом деле, я помню, как писал набор тестовв Си один раз это было примерно так:

typedef bool (*predicate) (const type *);

const char * argument;

bool do_foo (const type * t) {
    return bar (t, argument);
}

bool do_baz (const type * t) {
    return bap (t, argument);
}

predicate foo (const char * arg) {
    argument = arg;
    return do_foo;
}

predicate baz (const char * arg) {
    argument = arg;
    return do_baz;
}

assert (for_all (data_set("alpha"), foo ("abc")));
assert (for_all (data_set("beta"),  baz ("def")));

Все это было на чистом C, без хитрости макросов и т. д. Функциональный стиль и что-то вроде карри.Здесь преимущество в том, что вы можете сразу увидеть, каковы тестовые случаи.data_set похож - он связывает свой аргумент с другой функцией, которая извлекает данные: for_all выполняет thunk, проверяет предикат и очищает.Tidy.

...