2013-03-23 65 views
0

写在这个方便的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构造函数?

+0

嵌套的StateT非常奇怪。为什么不定义单个级别的StateT? – 2013-03-23 14:59:11

+0

@DonStewart woa你写过真实世界的哈斯克尔!但是,我解决了这个问题,所以现在有一个更合乎逻辑的进展。 P.S.,我也遇到了单个堆栈StateT的问题 – chibro2 2013-03-23 15:05:48

回答

3

脱糖版本嵌套示例的:

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可以执行自动脱糖,见this question

至于需要runIdentity,没有什么神秘的。你必须打开Monad堆栈的每一层以获得里面的值,并且Identity恰好在堆栈中。现在,State monad可以按照StateTIdentity来实现,但在这种情况下,用户被呈现为隐藏内部机器的“统一视图”。如果您检查transformers包中的runState的源代码,您会发现它在内部调用runIdentity

+0

这太棒了!谢谢你指点我到Lamdabot – chibro2 2013-03-23 15:27:39

相关问题