1

用这个方便的 do-notation 写的,这对我来说似乎很清楚。但我似乎无法让脱糖版本>>=用于工作,这令人担忧。有人可以用扩展符号重写这些吗?

未嵌套

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 构造函数?

4

1 回答 1

4

嵌套示例的脱糖版本:

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,这并没有什么神秘之处。您必须打开 monad 堆栈的每一层才能获得内部的值,并且Identity恰好在堆栈中。现在,Statemonad 可以用StateT和来实现Identity,但在这种情况下,用户会看到一个隐藏内部机制的“统一视图”。如果您检查包runState中的源代码transformers,您会看到它在runIdentity内部调用。

于 2013-03-23T15:23:21.557 回答