Может ли кто-нибудь удалить сахар из этой монады?
Написано в этой удобной нотации, мне это кажется довольно понятным. Но я не могу получить версию обессахаренного с помощью >>=
на работу, которая вызывает беспокойство. Может ли кто-нибудь переписать их в расширенной записи, пожалуйста?
Не вложенный
stack1 :: StateT Int Identity ( Int, Int )
stack1 = do
a <- get
put ( a + 1 )
b <- get
return ( a, b )
runstack1 :: ( Int, Int )
runstack1 = evalState stack1 11
Вложенные
stack3 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack3 = do
modify (+10)
lift $ modify ( ++ " world" )
lift . lift $ modify ( ++ " word" )
a <- get
b <- lift get
c <- lift . lift $ get
return ( a, b, c )
runStack3 :: ( Int, String, String )
runStack3 = runIdentity $ evalStateT ( evalStateT ( evalStateT runStack3 1 ) "hello" ) "harro"
Кроме того, сравнивая в сигнатуре runStack1 и runStack3, я могу понять, почему runIdentity
необходимо, но может ли кто-нибудь объяснить, почему это так, поскольку и stack1, и stack3 обертывают конструктор Identity?
1 ответ
Desugared версия вложенного примера:
stack4 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack4 = modify (+10) >>= \_ ->
(lift $ modify ( ++ " world" )) >>= \_ ->
(lift . lift $ modify ( ++ " word" )) >>= \_ ->
get >>= \a ->
lift get >>= \b ->
(lift . lift $ get) >>= \c ->
return (a,b,c)
В аппликативном стиле:
import Control.Applicative
stack5 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack5 = modify (+10) *>
(lift $ modify ( ++ " world" )) *>
(lift . lift $ modify ( ++ " word" )) *>
((,,) <$> get <*> lift get <*> (lift . lift $ get))
Кроме того, Lambdabot может выполнять автоматическое удаление ошибок, см. Этот вопрос.
Что касается необходимости runIdentity
в этом нет ничего загадочного. Вы должны развернуть каждый слой стека монады, чтобы получить значение внутри, и Identity
случается в стеке. Теперь State
Монада может быть реализована с точки зрения StateT
а также Identity
, но в этом случае пользователю предоставляется "унифицированный вид", который скрывает внутренний механизм. Если вы проверите исходный код для runState
в transformers
пакет, вы увидите, что он вызывает runIdentity
внутренне.