tick :: State Int Int
tick = get >>= \n ->
put (n+1) >>= \y ->
return n
我put (n+1)
对这个函数的最终结果有什么影响感到困惑。看起来这个函数应该返回初始状态不变。我试图在脑海中回顾这一点,但我一直没有足够的空间来固定东西。:\
如果有人可以指导我评估此功能,那将非常有帮助。
...如何将更新状态放在首位?它似乎只是坐在那里无所事事......
啊,现在我明白你的问题了。您想知道put
(和get
)如何工作,对吗?
也许 JavaScript 中的一个例子会有所帮助(一种具有实际可变状态的语言):
var s; // mutable state
function get() { return s; }
function put(x) { s = x; }
function tick() {
var n = get();
put(n + 1);
return n;
}
我希望这说明,虽然n
没有改变,但内部状态仍然会得到更新。如果执行tick()
两次,状态将增加两次。
回到Haskell,这里是State
monad(相关部分)的完整定义:
newtype State s a = State { runState :: s -> (a, s) }
instance Monad (State s) where
return a = State $ \s -> (a, s)
m >>= k = State $ \s -> let
(a, r) = runState m s
in runState (k a) r
get = State $ \s -> (s, s)
put s = State $ \_ -> ((), s)
现在尝试tick
通过手动内联>>=
、return
和来进一步扩展您get
的示例put
。希望它会更清楚 State 是如何运作的。
你完全正确。“函数”的“结果”tick
是状态的初始值。
当然,tick
现在不是真正的“函数”,而是可以在产生结果之前读取和写入状态的计算。
在这种情况下,状态会更新,但您仍会返回状态的原始值:
-- 4 is the inital state
ghci> runState tick 4
(4, 5)
-- 4 is the result of the tick computation, 5 is the updated state
在这种情况下,由于您不再检查内部状态tick
,因此您看不到更改的状态。但是,如果在 之后发生其他一些计算tick
,它可以看到更新的状态。
例如,执行tick
两次(第二次将读取更新后的状态):
-- 4 is the inital state
ghci> runState (tick >> tick) 4
(5, 6)
-- 5 is the result of the tick computation executed twice,
-- 6 is the updated state
do
使用符号来编写它可能会有所帮助
tick :: State Int Int
tick = do
n <- get -- get the state
put (n+1) -- save an incremented state
return n -- return the original state
虽然put (n+1)
不影响计算结果,但它确实改变了状态单子中的状态。