, используйте функцию
>state
, которая делает то же самое, что делал бы конструктор
>State
.
Теперь, когда вы увидели, в чём заключается суть вычислений с состоянием и как их можно даже воспринимать в виде значений с контекстами, давайте рассмотрим их экземпляр класса >Monad
:
>instance Monad (State s) where
> return x = State $ \s –> (x, s)
> (State h) >>= f = State $ \s –> let (a, newState) = h s
> (State g) = f a
> in g newState
Наша цель использования функции >return
состоит в том, чтобы взять значение и создать вычисление с состоянием, которое всегда содержит это значение в качестве своего результата. Поэтому мы просто создаём анонимную функцию >\s –> (x, s)
. Мы всегда представляем значение >x
в качестве результата вычисления с состоянием, а состояние остаётся неизменным, так как функция >return
должна помещать значение в минимальный контекст. Потому функция >return
создаст вычисление с состоянием, которое представляет определённое значение в качестве результата, а состояние сохраняет неизменным.
А что насчёт операции >>>=
? Ну что ж, результатом передачи вычисления с состоянием функции с помощью операции >>>=
должно быть вычисление с состоянием, верно? Поэтому мы начинаем с обёртки >newtype State
, а затем вызываем анонимную функцию. Эта анонимная функция будет нашим новым вычислением с состоянием. Но что же в ней происходит? Нам каким-то образом нужно извлечь значение результата из первого вычисления с состоянием. Поскольку прямо сейчас мы находимся в вычислении с состоянием, то можем передать вычислению с состоянием >h
наше текущее состояние >s
, что в результате даёт пару из результата и нового состояния: >(a,
>newState)
.
До сих пор каждый раз, когда мы реализовывали операцию >>>=
, сразу же после извлечения результата из монадического значения мы применяли к нему функцию >f
, чтобы получить новое монадическое значение. В случае с монадой >Writer
после того, как это сделано и получено новое монадическое значение, нам по-прежнему нужно позаботиться о контексте, объединив прежнее и новое моноидные значения с помощью функции >mappend
. Здесь мы выполняем вызов выражения >f a
и получаем новое вычисление с состоянием >g
. Теперь, когда у нас есть новое вычисление с состоянием и новое состояние (известное под именем >newState
), мы просто применяем это вычисление с состоянием >g
к >newState
. Результатом является кортеж из окончательного результата и окончательного состояния!
Итак, при использовании операции >>>=
мы как бы «склеиваем» друг с другом два вычисления, обладающих состоянием. Второе вычисление скрыто внутри функции, которая принимает результат предыдущего вычисления. Поскольку функции