Функциональные языки по определению не должны поддерживать переменные состояния. Почему же тогда 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 включают в себя более простую отладку, повторяемое модульное тестирование, безболезненный параллелизм и удобство многоядерности / графического процессора.