Это действительно сводится к пониманию того, что состояние изоморфно s -> (a, s)
. Таким образом, любое значение, «завернутое» в монадическое действие, является результатом применения преобразования к некоторому состоянию s
(вычисление с учетом состояния, производящее a
).
Передача состояния между двумя сохраняющих состояние вычислений
f :: a -> State s b
g :: b -> State s c
соответствует сочинял их >=>
f >=> g
или с помощью >>=
\a -> f a >>= g
результат здесь
a -> State s c
это действие, которое с сохранением состояния превращает некоторые основные состояния s
в некотором роде, разрешается доступ к некоторым a
и производит некоторую c
. Таким образом, полное преобразование может зависеть от a
, и значение c
может зависеть от некоторого состояния s
. Это именно то, что вы хотели бы выразить вычисления с учетом состояния. Чистая вещь (и единственная цель выражения этого механизма как монады) состоит в том, что вам не нужно беспокоиться о том, чтобы проехать мимо государства. Но чтобы понять, как это делается, пожалуйста, обратитесь к определению >>=
по адресу hackage), просто игнорируйте на мгновение, что это трансформатор, а не конечная монада).
m >>= k = StateT $ \ s -> do
~(a, s') <- runStateT m s
runStateT (k a) s'
вы можете игнорировать обертывание и разворачивание использованием StateT
и runStateT
, здесь m
в форме s -> (a, s)
, k
имеет форму a -> (s -> (b, s))
, и вы хотите, чтобы произвести динамическую трансформацию s -> (b, s)
. Таким образом, результат будет функцией от s
, чтобы произвести b
вы можете использовать k
, но вам нужно a
во-первых, как вы производите a
? вы можете принять m
и применить его к состоянию s
, вы получите измененное состояние s'
от первого монадического действия m
, и вы передадите это состояние в (k a)
(который имеет тип s -> (b, s)
). Именно здесь государство s
прошло через m
, чтобы стать s'
и передано в k
, чтобы стать окончательным s''
.
Для вас, как пользователя этого механизма, это остается скрытым, и это - аккуратная вещь о монадах. Если вы хотите, чтобы состояние эволюционировало по некоторым вычислениям, вы строите свои вычисления с небольших шагов, которые вы выражаете как State
-actions, и вы даете do
-notation или bind (>>=
), чтобы выполнить цепочку/прохождение.
Единственная разница между >>=
и >>
заключается в том, что вы заботитесь о нем или не заботитесь о негосударственном результате.
a >> b
фактически является эквивалентной
a >>= \_ -> b
так, что когда-либо значение приобретает выход под действием a
, вы бросаете его (сохраняя только модифицированное состояние) и продолжают (передавать состояние вместе) с другое действие b
.
Что касается вас примеров
tick :: State Int Int
tick = get >>= \n ->
put (n+1) >>
return n
вы можете переписать его в do
-notation в
tick = do
n <- get
put (n + 1)
return n
в то время как первый способ написания это делает, может быть более явным то, что передается, как, второй способ хорошо показывает, как вам не нужно заботиться об этом.
Первый get
ток состояние и выставить его (get :: s -> (s, s)
в упрощенной обстановке), то <-
говорит, что вы заботитесь о стоимости, и вы не хотите его выбросить, основное состояние также прошло в фоновом режиме без изменения (так работает get
).
Тогда put :: s -> (s -> ((), s))
, что эквивалентно после удаления ненужных скобок в put :: s -> s -> ((), s)
, принимает значение, чтобы заменить текущее состояние с (первым аргументом), и производит динамическое действие, результат которого является неинтересным значением ()
который вы уронили (потому что вы не используете <-
или потому, что используете >>
вместо >>=
). Из-за put
базовое состояние изменилось на n + 1
и как таковое оно передается.
return
ничего не делает для основного состояния, только возвращает свой аргумент.
Чтобы подвести итог, tick
начинается с некоторым начальным значением s
он обновляет его s+1
внутренне и выходы s
на стороне.
Другой пример работает точно так же, >>
используется только там, чтобы выбросить ()
от put
. Но состояние все время проходит.
спасибо! после прочтения я понимаю, что я просто не понял, что это ценность, которая отбрасывается, но состояние все равно проходит. –
рад помочь, если ответ на ваше удовлетворение, пожалуйста, подумайте о том, чтобы принять его, чтобы закрыть вопрос (это касается всех вопросов о stackoverflow), помогает отслеживать неразрешенные проблемы и помогает другим найти ответы, прежде чем задавать свои вопросы. – jakubdaniel
Спасибо за этот отзыв, тоже LOL –