Функціональні мови, за визначенням, не повинні підтримувати змінні стану. Чому тоді Haskell, Clojure та інші забезпечують реалізацію транзакційної пам'яті програмного забезпечення (STM)? Чи існує конфлікт між двома підходами?
Функціональні мови, за визначенням, не повинні підтримувати змінні стану. Чому тоді Haskell, Clojure та інші забезпечують реалізацію транзакційної пам'яті програмного забезпечення (STM)? Чи існує конфлікт між двома підходами?
Відповіді:
Немає нічого поганого у функціональній мові, що підтримує стан, що змінюється. Навіть "чисті" функціональні мови, такі як Haskell, повинні підтримувати стан, щоб взаємодіяти з реальним світом. "Нечисті" функціональні мови, такі як Clojure, дозволяють побічні ефекти, які можуть включати мутаційний стан.
Основний момент полягає в тому, що функціональні мови перешкоджають зміненному стану, якщо вам це справді не потрібно . Загальний стиль полягає в програмуванні з використанням чистих функцій та незмінних даних, а взаємодія лише з "нечистим" змінним станом у конкретних частинах вашого коду, які цього вимагають. Таким чином, ви можете зберегти решту бази коду «чистою».
Я думаю, що є кілька причин, чому STM є більш поширеним у функціональних мовах:
Мені особисто подобається підхід Clojure до можливості зміни змін, але лише в умовах жорстко контрольованих "керованих посилань", які можуть брати участь у транзакціях STM. Все інше в мові "чисто функціональне".
;; define two accounts as managed references
(def account-a (ref 100))
(def account-b (ref 100))
;; define a transactional "transfer" function
(defn transfer [ref-1 ref-2 amount]
(dosync
(if (>= @ref-1 amount)
(do
(alter ref-1 - amount)
(alter ref-2 + amount))
(throw (Error. "Insufficient balance!")))))
;; make a stranfer
(transfer account-a account-b 75)
;; inspect the accounts
@account-a
=> 25
@account-b
=> 175
Зверніть увагу, що наведений вище код є повністю трансакційним та атомним - зовнішній спостерігач, який читає два баланси в рамках іншої транзакції, завжди побачить послідовний атомний стан, тобто два баланси завжди будуть дорівнювати 200. З одночасністю на основі блокування це напрочуд складна проблема вирішити у великій складній системі з багатьма транзакційними утвореннями.
Для додаткового просвітлення Річ Хікі чудово справляється з поясненням STM Clojure у цьому відео
Функціональні мови, за визначенням, не повинні підтримувати змінні стану
Ваше визначення неправильне. Мова, яка не може підтримувати стан, просто не може бути використана.
Різниця між функціональними та імперативними мовами полягає не в тому, що одна з них має державну, а інша - ні. Це певним чином вони підтримують державу.
Імперативні мови розповсюджуються державою по всій програмі.
Функціональні мови явно ізолюють та підтримують стан за допомогою підписів типів. І саме тому вони забезпечують складні механізми управління державою, такі як STM.
Іноді для програми потрібен стан, що змінюється (наприклад, вміст бази даних для веб-програми), і було б чудово використовувати її, не втрачаючи переваг функціонального програмування. У нефункціональних мовах стан, що змінюється, пронизує все. Якщо ви зробите це явним за допомогою якогось спеціального API , тоді ви можете обмежити його невеликим ідентифікованим регіоном, тоді як все інше залишається чисто функціональним. Переваги FP включають більш просту налагодження, повторне тестування одиниць, безболісну одночасність і дружність з багатоядерними / графічними процесорами.