我在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的大小,并且无论序列中插入什么都不会更改。 (我已经测试了插入方法并且它可以工作)。
这是不是因为懒惰的评价?奇怪的执行顺序?或者这应该给出新的,更新的环境,我是否在其他地方做错了什么?谢谢你的帮助。
答案 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