Пояснення комбінаторів для працюючої людини


93

Що таке комбінатор ??

Це "функція чи визначення без вільних змінних" (як визначено в SO)?

Або як щодо цього: за словами Джона Х'юза у його відомій статті про Стрілки, «комбінатор - це функція, яка будує програмні фрагменти з фрагментів програми» , що є вигідним, оскільки «... програміст, використовуючи комбінатори, будує більшу частину бажаного програму автоматично, а не писати кожну деталь вручну ". Він продовжує це говорити mapі filterє двома поширеними прикладами таких комбінаторів.

Деякі комбінатори, які відповідають першому визначенню:

  • S
  • К
  • Y
  • інші з To Mock a Mockingbird (я можу помилятися - я не читав цю книгу)

Деякі комбінатори, які відповідають другому визначенню:

  • карта
  • фільтр
  • скласти / зменшити (імовірно)
  • будь-який з >> =, скласти, fmap ?????

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

  1. Які ще є приклади комбінаторів, таких як карта, фільтр?
  2. Які комбінатори часто застосовують мови програмування?
  3. Як комбінатори можуть допомогти мені розробити кращий API?
  4. Як створити ефективні комбінатори?
  5. Чим подібні комбінатори у нефункціональній мові (скажімо, Java), або що ці мови використовують замість комбінаторів?

Оновлення

Завдяки @CA McCann я тепер трохи краще розумію комбінаторів. Але одне питання досі є важливим моментом для мене:

Яка різниця між функціональною програмою, написаною за допомогою, та програмою, написаною без важкого використання комбінаторів?

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

Я також шукаю більше прикладів та пояснень складних комбінаторів (тобто складніших ніж fold) у загальних мовах програмування.


4
Я розглядаю карту / фільтр / складку функцій вищого порядку і я їх постійно використовую. Я досі не маю уявлення, що таке комбінатор.

1
@pst - Я б погодився 5 днів тому, але зараз я не можу посперечатися з Джоном Хьюзом
Метт Фенвік

Відповіді:


93

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

Два визначення - це одна і та ж річ. Перша заснована на формальному визначенні, і приклади, які ви наводите, - це примітивні комбінатори - найменші можливі будівельні блоки. Вони можуть допомогти вам написати справжню програму настільки, наскільки ви можете створити більш складні комбінатори. Подумайте про комбінатори, такі як S та K, як машинну мову гіпотетичного "комбінаційного комп'ютера". Фактичні комп'ютери не працюють таким чином, звичайно, тому на практиці у вас зазвичай операції вищого рівня реалізуються за лаштунками іншими способами, але концептуальна основа все ще є корисним інструментом для розуміння значення цих вищих рівнів операції.

Друге визначення, яке ви даєте, є більш неофіційним і щодо використання більш складних комбінаторів у вигляді функцій вищого порядку, які поєднують інші функції різними способами. Зауважте, що якщо основні будівельні блоки - це примітивні комбінатори, наведені вище, все , що будується з них, є функцією вищого порядку і комбінатором. Однак мовою, де існують інші примітиви, ви маєте відмінність між речами, які є або не є функціями. У цьому випадку комбінатор, як правило, визначається як функція, яка маніпулює іншими функціями загально, а не працює над будь-якими функціонувати безпосередньо.

Які ще є приклади комбінаторів, таких як карта, фільтр?

Занадто багато для переліку! Обидва перетворюють функцію, яка описує поведінку на одному значенні, у функцію, яка описує поведінку в цілій колекції. Ви також можете мати функції, які перетворюють лише інші функції, такі як їх складання в кінці або розділення та рекомбінація аргументів. Ви можете мати комбінатори, які перетворюють одномоментні операції в рекурсивні операції, які виробляють або споживають колекції. Або всілякі інші речі, насправді.

Які комбінатори часто застосовують мови програмування?

Це буде досить різнитися. Існує порівняно небагато абсолютно загальних комбінаторів - в основному первісних згаданих вище - тому в більшості випадків комбінатори матимуть певну інформацію про будь-які використовувані структури даних (навіть якщо ці структури даних так чи інакше побудовані з інших комбінаторів), в яких У випадку, як правило, існує декілька "повністю загальних" комбінаторів, а потім будь-які різні спеціалізовані форми хтось вирішив надати. Існує смішна кількість випадків, коли (відповідно узагальнені версії) карти, складання та розгортання вистачають, щоб зробити майже все, що ви хочете.

Як комбінатори можуть допомогти мені розробити кращий API?

Точно так, як ви сказали, думкою про операції високого рівня та про те, як вони взаємодіють, а не деталі низького рівня.

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

Як створити ефективні комбінатори?

Спочатку подумайте, які операції мають сенс для будь-яких даних, які використовує ваша програма. Потім подумайте, як ці операції можуть бути змістовно поєднані загальними способами, а також про те, як операції можна розбити на більш дрібні шматки, які з'єднані назад. Головне - працювати з перетвореннями і операціями , а не прямими діями . Якщо у вас є функція, яка просто непрозоро виконує якийсь складний біт функціональності і випльовує лише якийсь заздалегідь засвоєний результат, з цим не багато чого. Остаточні результати залиште в коді, який використовує комбінатори - ви хочете, щоб речі, які ведуть вас з точки А до точки В, а не речі, які очікують на початок чи кінець процесу.

Чим подібні комбінатори у нефункціональній мові (скажімо, Java), або що ці мови використовують замість комбінаторів?

Ахахахаха. Смішно запитати, адже об'єкти - це справді речі вищого порядку - вони мають деякі дані, але вони також здійснюють купу операцій, і досить багато того, що є гарним дизайном OOP, зводиться до "об'єктів повинні зазвичай діють як комбінатори, а не структури даних ".

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


Чудова відповідь! Подивимось, чи я це розумію - комбінатори не є особливими, а, скоріше, невід’ємною частиною побудови ремонтованої програмної системи? Я все ще намагаюся перетворити заяву Х'юза "фрагменти програми" у контексті Вашої публікації.
Метт Фенвік

@Matt Fenwick: Я майже впевнений, що він просто використовує "фрагменти програми", щоб мати на увазі те, яке саме перетворення / операції я говорю, особливо якщо мова йде про "Стрілки", які ще сильніше підкреслюють цей підхід. Крім того, я б не сказав, що мова йде саме про побудову ремонтопридатних систем - більше про побудову систем, які є високомодульними і певним чином роз'єднані , з перевагами, які тягне за собою.
CA McCann

Чи знаєте ви якісь хороші ресурси для читання практичного використання комбінаторів? Дякую купу!
Метт Фенвік

@Matt Fenwick: Нічого специфічного для цього у верхній частині голови, вибачте. Однак це, як правило, природний підхід для програм, написаних у дуже функціональному стилі, тому просто проводьте деякий час з мовами, які наполегливо заохочують це (наприклад, Haskell або Clojure), і дивлячись, як чистим та ідіоматичним кодом написано цією мовою , є досить хорошим способом відчути стиль.
CA McCann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.