Получение и установка состояния
Модуль Control.Monad.State определяет класс типов под названием MonadState, в котором присутствуют две весьма полезные функции: get и put. Для монады State функция get реализована вот так:
get = state $ s –> (s, s)
Она просто берёт текущее состояние и представляет его в качестве результата.
Функция put принимает некоторое состояние и создаёт функцию с состоянием, которая заменяет им текущее состояние:
put newState = state $ s –> ((), newState)
Поэтому, используя их, мы можем посмотреть, чему равен текущий стек, либо полностью заменить его другим стеком – например, так:
stackyStack :: State Stack ()
stackyStack = do
stackNow <– get
if stackNow == [1,2,3]
then put [8,3,1]
else put [9,2,1]
Также можно использовать функции get и put, чтобы реализовать функции pop и push. Вот определение функции pop:
pop :: State Stack Int
pop = do
(x:xs) <– get
put xs
return x
Мы используем функцию get, чтобы получить весь стек, а затем – функцию put, чтобы новым состоянием были все элементы за исключением верхнего. После чего прибегаем к функции return, чтобы представить значение x в качестве результата.
Вот определение функции push, реализованной с использованием get и put:
push :: Int –> State Stack ()
push x = do
xs <– get
put (x:xs)
Мы просто используем функцию get, чтобы получить текущее состояние, и функцию put, чтобы установить состояние в такое же, как наш стек с элементом x на вершине.
Стоит проверить, каким был бы тип операции >>=, если бы она работала только со значениями монады State:
(>>=) :: State s a –> (a –> State s b) –> State s b
Видите, как тип состояния s остаётся тем же, но тип результата может изменяться с a на b? Это означает, что мы можем «склеивать» вместе несколько вычислений с состоянием, результаты которых имеют различные типы, но тип состояния должен оставаться тем же. Почему же так?.. Ну, например, для типа Maybe операция >>= имеет такой тип:
(>>=) :: Maybe a –> (a –> Maybe b) –> Maybe b
Логично, что сама монада Maybe не изменяется. Не имело бы смысла использовать операцию >>= между двумя разными монадами. Для монады State монадой на самом деле является State s, так что если бы этот тип s был различным, мы использовали бы операцию >>= между двумя разными монадами.