Асинхронне програмування на функціональних мовах


31

Я в основному програміст C / C ++, а це означає, що більшість мого досвіду стосується процедурних та об'єктно-орієнтованих парадигм. Однак, як відомо багато програмістів на C ++, C ++ з роками перейшов до акцентів до функціонально-есківського стилю, що завершилося, нарешті, додаванням лямбда і закриттям в C ++ 0x.

Незважаючи на те, хоча я маю значний досвід кодування у функціональному стилі за допомогою C ++, у мене дуже мало досвіду з фактичними функціональними мовами, такими як Lisp, Haskell тощо.

Нещодавно я почав вивчати ці мови, тому що ідея "відсутність побічних ефектів" у суто функціональних мовах завжди мене заінтригувала, особливо що стосується її застосувань для одночасності та розподілу обчислень.

Однак, виходячи з фону C ++, я збентежений, як ця філософія "без побічних ефектів" працює з асинхронним програмуванням. Під асинхронним програмуванням я маю на увазі будь-який фреймворк / API / стиль кодування, який розсилає передбачені користувачем обробники подій для обробки подій, які відбуваються асинхронно (поза потоком програми). Це включає асинхронні бібліотеки, такі як Boost.ASIO, або навіть просто старий C обробники сигналів або обробники подій Java GUI.

Єдине, що усього цього спільного - це те, що, мабуть, характер асинхронного програмування вимагає створення побічних ефектів (стану) для того, щоб основний потік програми став зрозуміти, що викликається асинхронний обробник подій. Як правило, у такій структурі, як Boost.ASIO, обробник подій змінює стан об'єкта, так що ефект події поширюється поза межею функціонування обробника події. Дійсно, що ще може зробити обробник подій? Він не може "повернути" значення до точки виклику, оскільки немає точки дзвінка. Обробник подій не є частиною основного потоку програми, тому єдиний спосіб, який він може мати будь-який вплив на фактичну програму, - це змінити деякий стан (або інше longjmpна інший пункт виконання).

Тож здається, що асинхронне програмування - це все про асинхронне створення побічних ефектів. Це, здається, повністю не входить в цілі функціонального програмування. Як ці парадигми узгоджуються (на практиці) з функціональними мовами?


3
Нічого собі, я просто збирався написати таке питання, як це, і не знав, як його поставити, а потім побачив це в пропозиціях!
Amogh Talpallikar

Відповіді:


11

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

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

Функціональне програмування наголошує "немає побічних ефектів", коли це має сенс. Однак, щоб робити програмування в реальному світі, як ви сказали, вам потрібно змінити стан світу. (Наприклад, реагування на події, запис на диск тощо).

Для отримання додаткової інформації про асинхронне програмування на функціональних мовах, я настійно закликаю вас ознайомитись із моделлю програмування асинхронних робочих потоків F # . Це дозволяє писати функціональні програми, приховуючи всі безладні деталі переходу потоку в бібліотеці. (За способом, дуже схожим на монади у стилі Haskell.)

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


5
Також: погляд на Ерланг допомагає. Мова дуже простий, чистий (всі дані незмінний), і це все про асинхронної обробки.
9000

В основному після розуміння переваг функціонального підходу та зміни стану лише тоді, коли це має значення, автоматично переконайтеся, що навіть якщо ви працюєте в скажіть щось на зразок Java, ви знаєте, коли змінити стан і як тримати такі речі під контролем.
Amogh Talpallikar

Не погоджуючись - факт, що програма складається з «чистих» функцій, не означає, що вона не повторюється із зовнішнім світом, це означає, що кожна функція в програмі для одного набору аргументів завжди буде повертати той самий результат, і це (чистота) велика справа, адже, з практичного погляду - така програма буде менш глючною, більш «перевіряемою», успішне виконання функцій можна було б довести математично.
Gill Bates

8

Це захоплююче питання. На мою думку, найцікавішим є підхід, прийнятий у Clojure та пояснений у цьому відео:

http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

В основному пропоноване "рішення" полягає в наступному:

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

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


6

Одне зауваження: функціональна мова чиста, але час її виконання - ні.

Наприклад, час виконання Haskell передбачає черги, мультиплексування ниток, збирання сміття тощо ... все це не є чисто.

Хороший приклад - лінь. Haskell підтримує ледачу оцінку (це фактично дефолт). Ви створюєте значення лінивого, готуючи операцію, потім можете створити кілька копій цього значення, і воно все ще "ледаче", поки це не потрібно. Коли результат потрібен або якщо час виконання знайде деякий час, значення фактично обчислюється, і стан лінивого об’єкта змінюється, щоб відображати, що для його результату більше не потрібно проводити обчислення (ще раз). Зараз він доступний через усі посилання, тому стан об’єкта змінився, навіть якщо це чиста мова.


2

Мене бентежить, як ця філософія "без побічних ефектів" працює з асинхронним програмуванням. Під асинхронним програмуванням я маю на увазі ...

Тож був би сенс.

Звук, без побічних ефектів, не сумісний із рамками, які залежать від стану. Знайдіть нові рамки.

Наприклад, стандарт WSGI Python дозволяє нам створювати додатки без побічних ефектів.

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


"дозволяє створювати додатки без побічних ефектів" Я думаю, що десь там відсутнє слово.
Крістофер Махан

1

Дізнавшись інкапсуляції від Borland C ++ після вивчення C, коли Borland C ++ не вистачало шаблонів, які б увімкнули генеричні дані, парадигма орієнтації на об'єкт зробила мене непростим. Дещо природнішим способом обчислення здавалося фільтрування даних по трубах. Зовнішній потік мав окрему і незалежну ідентичність від вхідного непорушного вхідного потоку, а не вважати його побічним ефектом, тобто кожне джерело даних (або фільтр) було автономним від інших. Натискання клавіші (приклад події) обмежувало асинхронні комбінації вводу користувача доступними кодовими клавішами. Функції працюють на аргументах вхідних параметрів, і стан, інкапсульований класом, є лише ярликом, щоб уникнути явного передачі повторюваних аргументів між невеликими підмножинами функцій, окрім того, щоб бути обережними для пов'язаного контексту, що запобігає зловживанню цими аргументами будь-якої довільної функції.

Жорстке дотримання певної парадигми спричиняє незручності мати справу з непрохідними абстракціями, наприклад. комерційні режими виконання, такі як JRE, DirectX, .net цільові об'єктно-орієнтовані прихильники передусім. Щоб обмежити незручності, мови вибирають або вчені складні монади, як Haskell, або гнучку підтримку багато парадигми, як F # врешті-решт. Якщо інкапсуляція не є корисною у випадку використання декількох випадків використання успадкованих даних, підхід з багато парадигмою може бути найкращою альтернативою деяким, іноді складним, парадигмним моделям програмування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.