Haskell State monad:在序列

时间:2018-01-19 22:07:09

标签: haskell state monads

我在haskell中构建一个C#编译器,作为编译器课程的一部分。 我正在使用状态monad,问题在于块的代码。我正在使用状态来包装声明的变量的env。在解析块时,我想扩展这个状态(因为块内的声明),但之后返回到原始块(因为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)

env的类型为Data.Map。

我的问题是newEnv不是序列后更新的env,而是等于origEnv。因此,返回的大小100%取决于原始env的大小,并且无论序列中插入什么都不会更改。 (我已经测试了插入方法并且它可以工作)。

这是不是因为懒惰的评价?奇怪的执行顺序?或者这应该给出新的,更新的环境,我是否在其他地方做错了什么?谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

正如Justin L.所说,问题可能是block没有改变Env。这是您的示例代码,未更改,添加了一些测试代码以生成完整的程序。函数setValue生成EnvState,用于更改Env。在setValue 的调用中使用fStatBlock 导致的尺寸大于基于原始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