Haskell 状态 monad:在序列之后获取更新的状态

Haskell State monad: get updated state after sequence

作为编译器课程的一部分,我正在 haskell 构建一个 C# 编译器。 我正在使用状态 monad,问题出在块的代码中。我正在使用状态来包装声明的变量的环境。解析块时,我想扩展此状态(因为块内的声明),但之后 return 到原始块(因为 declerations 不在块外)。但是,我想先知道新更新状态的大小。所以我有以下代码:

type EnvState = State Env (Int, Code)
type Env = M.Map String Int

fStatBlock :: [EnvState] -> EnvState
fStatBlock block = do origEnv <- get
                      xs      <- sequence block -- prelude sequence
                      newEnv  <- get
                      put origEnv
                      return (M.size newEnv, concatMap snd xs)

环境类型为 Data.Map。

我的问题是 newEnv 不是序列后更新的环境,而是等于 origEnv。因此,returned 的大小 100% 取决于原始 env 的大小,并且无论序列中插入什么都不会改变。 (我已经测试了插入方法并且有效)。

这是否是由于惰性求值造成的?奇怪的执行顺序?或者这应该提供新的、更新的环境,我在其他地方做错了吗?感谢您的帮助。

正如 Justin L. 所建议的,问题可能是 block 没有改变 Env。这是您的示例代码,未更改,添加了一些测试代码以构成完整的程序。函数 setValue 产生一个 EnvState 来改变 Env。在对 fStatBlock 的调用中使用 setValue 确实 会导致比基于原始 env 的大小大一倍的大小。

完整程序

import Control.Monad.State 
import Data.Map as M

type Code = [Int]


-- begin original code ------

type EnvState = State Env (Int, Code)
type Env = M.Map String Int

fStatBlock :: [EnvState] -> EnvState
fStatBlock block = do origEnv <- get
                      xs      <- sequence block -- prelude sequence
                      newEnv  <- get
                      put origEnv
                      return (M.size newEnv, concatMap snd xs)

-- end original code -------


setValue :: String -> Int -> EnvState
setValue name value = state (\env -> ((0,[]), insert name value env))

main = do
    let env = fromList [("x", 5), ("y", 10)]
        fsb = fStatBlock [setValue "a" 15] 
    print $ fst $ fst $ runState fsb env

结果

$ ./main 
3