Я собираюсь ответить на ваш второй вопрос. На самом деле существует множество способов обработки изменчивого состояния в Haskell (и других языках FP). Прежде всего, Haskell поддерживает изменчивое состояние в IO, через конструкции IORef
и mvar
. Используя их, они будут хорошо знакомы программистам с императивных языков. Существуют также специализированные версии, такие как STRef
и TMVar
, а также изменяемые массивы, указатели и различные другие изменяемые данные. Самый большой недостаток состоит в том, что они обычно доступны только в IO или более специализированной монаде.
Наиболее распространенный способ моделирования состояния на функциональном языке явно передает состояние как аргумент функции и возвращаемое значение. Например:
randomGen :: Seed -> (Int, Seed)
Здесь randomGen
принимает параметр семян и возвращает новое семя. Каждый раз, когда вы его вызываете, вам нужно следить за семенем для следующей итерации. Этот метод всегда доступен для передачи состояния, но он быстро становится утомительным.
Возможно, наиболее распространенным методом Haskell является использование монады для инкапсуляции этого состояния. Мы можем заменить randomGen
с этим:
-- a Random monad is simply a Seed value as state
type Random a = State Seed a
randomGen2 :: Random Int
randomGen2 = do
seed <- get
let (x,seed') = randomGen seed
put seed'
return x
Теперь любые функции, которые нуждаются в ГСЧ может работать в случайном порядке монады просить их по мере необходимости. Вам просто нужно предоставить начальное состояние и вычисление.
runRandomComputation :: Random a -> Seed -> a
runRandomComputation = evalState
(обратите внимание, что есть функции, которые значительно сокращают определение randomGen2; я выбрал наиболее явную версию).
Если вашему случайному вычислению также необходим доступ к IO
, то вы используете версию трансформатора монады государства, StateT
.
Особо следует отметить монаду ST
, которая по существу обеспечивает механизм для инкапсуляции IO-специфических мутаций в сторону от остальной части IO. Монада ST предоставляет STRefs, которые являются изменяемой ссылкой на данные, а также изменяемыми массивами.Используя ST, можно определить такие вещи, как это:
randomList :: Seed -> [Int]
где [Int] бесконечный список случайных чисел (это будет цикл в конечном счете, в зависимости от вашего PSRG) от начальной семени вы даете ему.
И наконец, есть Functional Reactive Programming. Вероятно, самыми известными в настоящее время библиотеками для этого являются Yampa и Reactive, но другие заслуживают внимания. Существует несколько подходов к изменяемому состоянию в рамках различных реализаций FRP; из-за моего небольшого использования они часто кажутся похожими на концепцию сигнальной структуры, как в QT или Gtk + (например, добавление слушателей для событий).
Теперь, для первого вопроса. Для меня самым большим преимуществом является то, что изменяемое состояние отделено от другого кода на уровне типа. Это означает, что код не может случайно изменить состояние, если он явно не упоминается в сигнатуре типа. Он также дает очень хороший контроль состояния только для чтения в сравнении с изменчивым состоянием (монада-монада и монада монахов). Мне очень полезно структурировать свой код таким образом, и полезно быть в состоянии сказать только от сигнатуры типа, если функция может быть мутировало состояние неожиданно.
У меня лично нет никаких оговорок относительно использования изменчивого состояния в Haskell. Самая большая трудность заключается в том, что было бы утомительно добавлять состояние к тому, что раньше не требовалось, но то же самое было бы утомительно в других языках, которые я использовал для подобных задач (C#, Python).
http://www.haskell.org/all_about_monads/html/statemonad.html –
Управление состоянием в функциональном языке включает передачу состояния в функции. Монады упрощают это. – tylermac
@tylermac я никогда не мог понять монады, ну, я не могу сказать, что я глуп, по крайней мере, я BS в CS, но монады ... вы знаете хорошие уроки? – Andrey