IORef 更新后仍然引用旧值
IORef still refers to the old value after update
背景
我是Schemer,开始学习Haskell。我正在尝试在 C 中实现一个 Scheme 解释器,遵循 chapter 4 of SICP. It turns out programming directly in C is too hard. So I decide to first prototype in Haskell. With the help of Write Yourself a Scheme in 48 Hours,我已经实现了除变量、闭包和环境之外的所有内容。
问题
对 IORef
的修改不会在 main
的调用之间持续存在。我希望程序打印 (False) (True) (True) (True)... 但实际上它打印 (False) (True) (False) (真)(假)(真)...
代码的简化版本:
import Data.IORef
data SCM = Environment (IORef Bool) SCM | Empty'Environment
global :: IO SCM
global = Environment <$> newIORef False <*> pure Empty'Environment
print'' :: SCM -> IO ()
print'' ls =
case ls of
Empty'Environment -> pure ()
Environment first rest -> readIORef first >>= putStr . show >> print'' rest
print' :: SCM -> IO ()
print' ls = putStr "(" *> print'' ls *> putStrLn ")"
main :: IO ()
main = global >>=
\ls -> case ls of
Empty'Environment -> pure ()
Environment first _ -> print' ls *>
modifyIORef first (const True) *>
print' ls *>
main
语法高亮版本:
感谢您的帮助!
我们可以将您的示例缩减为 main = (global >>= loop) >> main
。问题是 global
不是单个全局变量,而是一个 IO SCM
,一个将创建全局值的操作。让我们重命名它:
createSCM :: IO SCM
createSCM = Environment <$> newIORef False <*> pure Empty'Environment
现在这个名字更接近真相了。每次调用该函数时,我们都会创建一个新的 SCM
。所以你的程序是这样工作的:
main = (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> ...
如您所见,我们一直在创建新的 SCM
。因此,您没有得到预期的行为。
解决方法很简单。显式创建 global
和 loop
:
main = do
global <- createSCM
let loop = do
...
loop
loop
背景
我是Schemer,开始学习Haskell。我正在尝试在 C 中实现一个 Scheme 解释器,遵循 chapter 4 of SICP. It turns out programming directly in C is too hard. So I decide to first prototype in Haskell. With the help of Write Yourself a Scheme in 48 Hours,我已经实现了除变量、闭包和环境之外的所有内容。
问题
对 IORef
的修改不会在 main
的调用之间持续存在。我希望程序打印 (False) (True) (True) (True)... 但实际上它打印 (False) (True) (False) (真)(假)(真)...
代码的简化版本:
import Data.IORef
data SCM = Environment (IORef Bool) SCM | Empty'Environment
global :: IO SCM
global = Environment <$> newIORef False <*> pure Empty'Environment
print'' :: SCM -> IO ()
print'' ls =
case ls of
Empty'Environment -> pure ()
Environment first rest -> readIORef first >>= putStr . show >> print'' rest
print' :: SCM -> IO ()
print' ls = putStr "(" *> print'' ls *> putStrLn ")"
main :: IO ()
main = global >>=
\ls -> case ls of
Empty'Environment -> pure ()
Environment first _ -> print' ls *>
modifyIORef first (const True) *>
print' ls *>
main
语法高亮版本:
感谢您的帮助!
我们可以将您的示例缩减为 main = (global >>= loop) >> main
。问题是 global
不是单个全局变量,而是一个 IO SCM
,一个将创建全局值的操作。让我们重命名它:
createSCM :: IO SCM
createSCM = Environment <$> newIORef False <*> pure Empty'Environment
现在这个名字更接近真相了。每次调用该函数时,我们都会创建一个新的 SCM
。所以你的程序是这样工作的:
main = (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> ...
如您所见,我们一直在创建新的 SCM
。因此,您没有得到预期的行为。
解决方法很简单。显式创建 global
和 loop
:
main = do
global <- createSCM
let loop = do
...
loop
loop