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
作为编译器课程的一部分,我正在 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