我在弄清楚 /how/ 绑定运算符实际上将以下 State monad 绑定在一起时遇到了一些麻烦:
pop :: State [Int] Int
pop = do
(x:xs) <- get
put xs
return x
push :: Int -> State [Int] ()
push x = do
xs <- get
put (x:xs)
doStuff :: State [Int] ()
doStuff = do
pop
x <- pop
push 5
push x
Take doStuff
,它可以被脱糖为以下内容:
pop >>= (\_ -> pop >>= (\x -> push 5 >>= (\_ -> push x)))
评估此行时,绑定实际发生的顺序是什么?因为,要实际绑定,Haskell 需要从运算符右侧的函数中获取 State monad >>=
(即,需要首先对函数右侧操作数进行完全评估),所以我认为会发生以下情况:
- s1 =
push 5 >>= (\_ -> push x)
- s2 =
pop >>= (\x -> s1)
- s3 =
pop >>= (\_ -> s2)
这是正确的思考方式吗?我觉得我很了解 monad,但我最大的问题是实际上可视化“幕后”发生的事情以及数据如何流动,可以这么说。这个do
符号给人一种我正在处理一堆顺序操作的错觉,而事实上,有一大堆嵌套和闭包。
我觉得我在这里想多了,结果让自己更加困惑。