You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

状态Monad中较新的状态更新为何不会被旧状态覆盖?

状态Monad中状态更新不会被更早状态覆盖的原因分析

你的核心误区是搞反了状态的传递方向——不管是标准State Monad的>>=,还是《Real World Haskell》里的==>,都是前一步输出的新状态作为后一步的输入状态,不存在“更早状态覆盖新状态”的情况,下面一步步拆解:

1. 先看《Learn You a Haskell》的State Monad定义

instance Monad (State s) where
    return x = State $ \s -> (x,s)
    (State h) >>= f = State $ \s -> let (a, newState) = h s
                                        (State g) = f a
                                    in  g newState
  • return逻辑简单:把值和原状态打包,不修改状态。
  • >>=的核心逻辑:
    1. 先执行第一个State动作h,传入初始状态s,得到结果a和更新后的状态newState。
    2. 把结果a传给函数f,得到第二个State动作g。
    3. 最后用**newState(而非初始的s)**去执行g。

也就是说,后一步的State动作是在前一步更新后的状态基础上运行的,新状态只会被后续步骤继承,不会被更早的初始状态覆盖。

2. 再看《Real World Haskell》的==>逻辑(Monad >>=的雏形)

(==>) :: Parse a -> (a -> Parse b) -> Parse b
firstParser ==> secondParser  =  Parse chainedParser
  where chainedParser initState   =
          case runParse firstParser initState of
            Left errMessage ->
                Left errMessage
            Right (firstResult, newState) ->
                runParse (secondParser firstResult) newState

==>的逻辑和>>=完全一致:

  • 先运行第一个Parser,传入初始状态initState,得到结果firstResult和更新后的newState。
  • 然后用这个newState去运行第二个Parser(由firstResult生成的)。

3. 具体拆解parseByte的执行流程

我们一步步走parseByte的运行过程(假设初始状态为initState0):

parseByte =
    getState ==> \initState ->
    case L.uncons (string initState) of
      Nothing ->
          bail "no more input"
      Just (byte,remainder) ->
          putState newState ==> \_ ->
          identity byte
        where newState = initState { string = remainder,
                                     offset = newOffset }
              newOffset = offset initState + 1
  1. 第一步:getState ==> ...

    • getState的作用是读取当前状态,所以runParse getState initState0会返回Right (initState0, initState0)(读取到的结果是初始状态,状态本身未修改)。
    • 接下来把读取到的initState0传给后面的lambda函数,进入case判断。
  2. 第二步:计算newState并执行putState newState ==> ...

    • 当有输入时,用读取到的initState0计算出newState(更新了字符串剩余部分和偏移量)。
    • 现在执行putState newState ==> \_ -> identity byte:
      • 先运行putState newState,传入的状态是第一步输出的initState0,runParse (putState newState) initState0会返回Right ((), newState)(putState的作用是把状态设置为newState,结果为())。
      • 然后用这个newState去运行后面的identity byte,runParse (identity byte) newState返回Right (byte, newState)(identity不修改状态,直接返回byte和当前状态)。

整个流程结束后,最终的状态是newState——也就是更新后的状态,初始的initState0只是用来生成新状态的中间值,根本不会去覆盖后续的新状态。

本质上,状态是线性传递的:每一步的状态输出都是下一步的输入,新状态只会被后续步骤继承,不存在更早状态回滚覆盖的情况。

内容的提问来源于stack exchange,提问作者planarian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:15:13