Объяснение комбинаторов для рабочего человека

Что такое комбинатор

Является ли функцией или определением без свободных переменных? (как определено в SO)?

Или как насчет этого: согласно John Hughes в его хорошо известной статье о стрелках, "комбинатор - это функция, которая строит программные фрагменты из программы фрагменты" , что выгодно, потому что "... программист, использующий комбинаторы, автоматически создает большую часть желаемой программы, а не записывает каждую деталь вручную". Далее он говорит, что map и filter - два распространенных примера таких комбинаторов.

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

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

  • Карта
  • фильтр
  • fold/reduce (предположительно)
  • любой из → =, compose, fmap?????

Меня не интересует первое определение - это не поможет мне написать настоящую программу (+1, если вы убедите меня, что я ошибаюсь). Пожалуйста, помогите мне понять второе определение. Я думаю, что карта, фильтр и сокращение полезны: они позволяют мне программировать на более высоком уровне - меньше ошибок, более короткий и понятный код. Вот некоторые из моих конкретных вопросов о комбинаторах:

  • Каковы примеры таких комбинаторов, как map, filter?
  • Какие компиляторы часто используют языки программирования?
  • Как комбатанты могут помочь мне разработать лучший API?
  • Как создать эффективные комбинаторы?
  • Что такое комбинаторы, похожие на нефункциональный язык (скажем, Java) или то, что эти языки используют вместо комбинаторов?

Обновить

Благодаря @C. А. Макканн, теперь у меня есть несколько лучшее понимание комбинаторов. Но для меня остается один вопрос:

В чем разница между функциональной программой, написанной с помощью одного, без использования большого количества комбинаторов?

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

Я также ищу больше примеров и объяснений сложных комбинаторов (т.е. более сложных, чем fold) в общих языках программирования.

Ответ 1

Меня не интересует первое определение - это не поможет мне написать настоящую программу (+1, если вы убедите меня, что я ошибаюсь). Пожалуйста, помогите мне понять второе определение. Я думаю, что карта, фильтр и сокращение полезны: они позволяют мне программировать на более высоком уровне - меньше ошибок, более короткий и понятный код.

Два определения в основном одно и то же. Первый основан на формальном определении, а примеры, которые вы даете, являются примитивными комбинаторами - возможны минимальные строительные блоки. Они могут помочь вам написать настоящую программу, поскольку с ними вы можете создавать более сложные комбинаторы. Подумайте о комбинаторах, таких как S и K, в качестве машинного языка гипотетического "комбинаторного компьютера". Разумеется, фактические компьютеры не работают таким образом, поэтому на практике вы обычно выполняете операции более высокого уровня за кулисами другими способами, но концептуальная основа по-прежнему является полезным инструментом для понимания смысла этих более высоких уровней операции.

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

Каковы еще примеры комбинаторов, таких как карта, фильтр?

Слишком много, чтобы перечислить! Оба из них преобразуют функцию, описывающую поведение по одному значению в функцию, описывающую поведение во всей коллекции. Вы также можете иметь функции, которые преобразуют только другие функции, такие как составление их из конца в конец, или разделение и рекомбинация аргументов. У вас есть комбинаторы, которые превращают одноступенчатые операции в рекурсивные операции, которые производят или потребляют коллекции. Или всевозможные другие вещи, действительно.

Какие компиляторы часто используют языки программирования?

Это будет немного отличаться. Существует относительно немного полностью общих комбинаторов - в основном, примитивных, упомянутых выше, - поэтому в большинстве случаев комбинаторы будут иметь некоторое представление о любых используемых структурах данных (даже если эти структуры данных построены из других комбинаторов в любом случае), в которых в случае, как правило, имеется несколько "полностью общих" комбинаторов, а затем любые различные специализированные формы, которые кто-то решил предоставить. Существует нелепое количество случаев, когда (подходящим образом обобщенные версии) карты, складываются и разворачиваются достаточно, чтобы делать почти все, что вы, возможно, захотите.

Как комбинаторы помогут мне разработать лучший API?

Точно, как вы сказали, думая в терминах операций высокого уровня и того, как они взаимодействуют, вместо деталей низкого уровня.

Подумайте о популярности "для каждого" -типных циклов над коллекциями, которые позволяют вам абстрагироваться от деталей перечисления коллекции. В большинстве случаев это всего лишь операции с картами/сгибами, и, делая это комбинатор (а не встроенный синтаксис), вы можете делать такие вещи, как принимать два существующих цикла и напрямую комбинировать их несколькими способами - вставлять одно внутри другого, делать один за другим и т.д. - просто применяя комбинатор, а не жонглируя целой связкой кода.

Как создать эффективные комбинаторы?

Во-первых, подумайте о том, какие операции имеют смысл в отношении любых данных, которые использует ваша программа. Затем подумайте о том, как эти операции могут быть целенаправленно объединены в общих целях, а также о том, как операции можно разбить на более мелкие куски, которые соединяются вместе. Главное - работать с преобразованиями и операциями, а не с прямыми действиями. Когда у вас есть функция, которая просто выполняет некоторые сложные функции в непрозрачном виде и только выплескивает какой-то предварительный результат, вы не можете с этим справиться. Оставьте окончательные результаты в коде, который использует комбинаторы - вам нужны вещи, которые доставят вас из точки A в точку B, а не из тех, которые ожидают начало или конец процесса.

Что такое комбинаторы, похожие на нефункциональный язык (скажем, Java), или то, что эти языки используют вместо комбинаторов?

Ахахахаха. Забавно, что вы должны спросить, потому что объекты - это, прежде всего, предметы более высокого порядка - у них есть некоторые данные, но они также несут вокруг кучу операций, и довольно много того, что составляет хороший дизайн ООП, сводится к "объектам обычно действуют как комбинаторы, а не структуры данных".

Итак, лучший ответ здесь заключается в том, что вместо комбинаторных вещей они используют классы с большим количеством методов getter и setter или публичных полей и логику, которая в основном состоит из выполнения непрозрачного предопределенного действия.