7
tick :: State Int Int
tick = get >>= \n ->
       put (n+1) >>= \y ->
       return n

put (n+1)对这个函数的最终结果有什么影响感到困惑。看起来这个函数应该返回初始状态不变。我试图在脑海中回顾这一点,但我一直没有足够的空间来固定东西。:\

如果有人可以指导我评估此功能,那将非常有帮助。

4

3 回答 3

10

...如何将更新状态放在首位?它似乎只是坐在那里无所事事......

啊,现在我明白你的问题了。您想知道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,这里是Statemonad(相关部分)的完整定义:

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 是如何运作的。

于 2009-11-25T12:14:56.663 回答
7

你完全正确。“函数”的“结果”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
于 2009-11-25T10:25:06.613 回答
5

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)不影响计算结果,但它确实改变了状态单子中的状态。

于 2009-11-25T10:31:36.593 回答