Разница между State, ST, IORef и MVar
Я работаю через напишите себе схему за 48 часов (Я до 85 часов), и я добрался до части о добавление переменных и задания. В этой главе есть большой концептуальный скачок, и я хотел бы, чтобы это было сделано в два этапа с хорошим рефакторингом между ними, а не прыгать прямо к окончательному решению. В любом случае...
Я заблудился с несколькими различными классами, которые, кажется, служат одной и той же цели: State, ST,IORef и MVar. Первые три упоминаются в тексте, в то время как последний, по-видимому, является предпочтительным ответом на многие вопросы StackOverflow о первых трех. Все они, кажется, несут состояние между последовательными вызовами.
что каждый из них и чем они отличаются друг от друга?
в частности, эти предложения не имеют смысла:
вместо этого мы используем функцию под названием государство threads, позволяя Haskell управлять агрегатным состоянием для нас. Это позволяет нам обрабатывать изменяемые переменные, как и в любом другом языке программирования, используя функции для получения или установки переменных.
и
модуль IORef позволяет использовать переменные с отслеживанием состояния в монаду IO.
все это делает строку type ENV = IORef [(String, IORef LispVal)] непонятно, почему второй IORef? Что сломается, если я напишу type ENV = State [(String, LispVal)] вместо этого?
3 ответов:
Монада состояния: модель изменчивого состояния
монада состояния-это чисто функциональная среда для программ с состоянием, с простым API:
- get
- поставить
документация пакет mtl.
монада состояния обычно используется, когда требуется состояние в одном потоке управления. Он фактически не использует изменяемое состояние в своей реализации. Вместо этого программа параметризуется значением состояния (т. е. состояние является дополнительным параметром для всех вычислений). Состояние только кажется мутированным в одном потоке (и не может быть разделено между потоками).
святая монада и Стрефы
ST монада является ограниченным двоюродным братом монады IO.
это позволяет произвольно государство-изменчивый, реализовано как фактическая изменяемая память на машине. API-интерфейс выполнен в безопасный побочный эффект-бесплатных программ, поскольку параметр типа rank-2 предотвращает выход значений, зависящих от изменяемого состояния, из локальной области.
таким образом, он позволяет контролировать изменчивость в других чистых программах.
обычно используется для изменяемых массивов и других структур данных, которые мутируют, а затем замораживаются. Это также очень эффективно, так как изменчивое состояние является "аппаратным ускорением".
основной API:
- Control.Monad.ST
- runST -- start a новое вычисление эффекта памяти.
- и STRefs: указатели на (локальные) изменяемые ячейки.
- St-основанные массивы (такие как вектор) также распространены.
думайте об этом как о менее опасном брате монады ИО. Или IO, где вы можете только читать и записывать в память.
IORef: STRefs in IO
Это STRefs (см. выше) в монаде IO. Они не имеют такие же гарантии безопасности как STRefs около местность.
MVars : IORefs с замками
Как STRefs или IORefs, но с замком прилагается, для безопасного одновременно доступ из нескольких потоков. IORefs и STRefs используются только безопасные в многопоточной установки при использовании
atomicModifyIORef(атомарная операция сравнения и замены). MVars-это более общий механизм безопасного совместного использования изменяемого состояния.вообще, в Haskell, используйте MVars или TVars (STM-based mutable cells), над STRef или Выводе ioref.
хорошо, я начну с
IORef.IORefпредоставляет значение, которое является изменяемым в монаде ввода-вывода. Это просто ссылка на некоторые данные, и, как и любая ссылка, есть функции, которые позволяют изменять данные, на которые она ссылается. В Haskell, все эти функции работают вIO. Вы можете думать об этом как о базе данных, файле или другом внешнем хранилище данных - вы можете получить и установить данные в нем, но для этого требуется пройти через IO. Причина ИО необходимо на все это, потому что Хаскелл чисто; компилятор должен знать, на какие данные указывает ссылка в любой момент времени (read сигнала sigfpe "ты мог бы придумать монады" blogpost).
MVars в основном то же самое, что и IORef, за исключением двух очень важных различий.MVar- это параллелизм примитивно, поэтому он предназначен для доступа из нескольких потоков. Второе отличие заключается в том, чтоMVarэто коробка, которая может быть полной или пустой. Так где жеIORef Intвсегда имеетInt(или внизу), anMVar Intможет бытьIntили он может быть пустым. Если поток пытается прочитать значение из пустогоMVar, он будет блокировать до тех пор, покаMVarзаполняется (другим потоком). В основном этоMVar aэквивалентноIORef (Maybe a)С дополнительной семантикой, которая полезна для параллелизма.
Stateэто монада, которая обеспечивает изменчивое состояние, не обязательно с IO. На самом деле, это особенно полезно для чистых вычислений. Если у вас есть алгоритм это использует состояние, но неIO, aStateмонада часто является элегантным решением.существует также версия трансформатора монады состояния,
StateT. Это часто используется для хранения данных конфигурации программы или типов состояния" игровой мир " в приложениях.
STнемного другое. Основная структура данных вST- этоSTRef, что похоже наIORefно с другой монады. ЭлементSTмонада использует тип системы обман ("потоки состояний", упомянутые в документах), чтобы гарантировать, что изменяемые данные не могут избежать монады; то есть, когда вы запускаете вычисление ST, вы получаете чистый результат. Причина St интересна тем, что это примитивная монада, подобная IO, позволяющая вычислениям выполнять низкоуровневые манипуляции на bytearrays и указателях. Это значит, чтоSTможет обеспечить чистый интерфейс при использовании низкоуровневых операций с изменяемыми данными, что означает, что это очень быстро. С точки зрения программы, это как если бы элементSTрасчет выполняется в отдельном потоке с локальной памятью потока.
другие сделали основные вещи, но чтобы ответить на прямой вопрос:
все это делает тип строка
ENV = IORef [(String, IORef LispVal)]запутанным. Почему второй выводе ioref? Что? сломается, если я сделаюtype ENV = State [(String, LispVal)]вместо?Lisp-это функциональный язык с изменяемым состоянием и лексической областью. Представьте, что вы закрыли над изменяемой переменной. Теперь у вас есть ссылка на эту переменную, висящую внутри какой-то другой функции-скажем (в псевдокоде в стиле Хаскелла)
(printIt, setIt) = let x = 5 in (\ () -> print x, \y -> set x y). Теперь у вас есть две функции-одна печатает x, а другая устанавливает его значение. Когда вы оцениваетеprintIt, вы хотите найти имя x в начальной среде, в которойprintItбыл определен, но вы хотите, чтобы поиск стоимостью это имя привязано к среде, в которойprintIt- это под названием (послеsetItможет быть вызвано любое количество раз).есть способы, кроме двух IORefs, чтобы сделать это, но вам, конечно, нужно больше чем последний тип, который вы предложили, который не позволяет вам изменять значения, к которым привязаны имена в лексически ограниченном виде. Google "проблема funargs" для целого ряда интересных предысторий.
Comments