IORef仍然引用更新后的旧值

时间:2017-08-21 11:20:58

标签: haskell io scheme ioref

背景

我是一名开始学习Haskell的Schemer。我试图在SICP的chapter 4之后用C实现一个Scheme解释器。事实证明直接用C编程太难了。所以我决定先在Haskell中进行原型设计。在Write Yourself a Scheme in 48 Hours的帮助下,我实现了除变量,闭包和环境之外的所有内容。

问题

IORef的调用之间,对main的修改不会持续存在。我希望程序打印(False)(True)(True)(True) ...但实际上它打印(False)(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

语法高亮版本: ioref.hs

感谢您的帮助!

1 个答案:

答案 0 :(得分:7)

我们可以将您的示例缩减到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。因此,您无法获得预期的行为。

解决方案很简单。明确地创建您的globalloop

main = do
  global <- createSCM
  let loop = do
         ...
         loop
  loop