使用 IORef 的全局状态,为什么这不起作用?



为了便于理解,我摆弄了一下IORef,并试图想出一个接近全局可变状态的东西:

import Data.IORef
x :: IO (IORef Int)
x = newIORef 0
incrAndPrint :: IO ()
incrAndPrint = do z <- x
modifyIORef z (+1)
readIORef z >>= putStrLn . show
main :: IO ()
main = incrAndPrint >> incrAndPrint

然而,令我惊讶的是,这打印

1
1

1
2

有人能解释一下为什么吗?此外,是否有一种方法可以让它"起作用"?如果不是,为什么?

你的x是一个IO动作,创建一个新的IORef Int,所以当你使用它时,它将始终创建一个新的从0开始。您可以通过两次增加相同的引用来轻松地实现此工作:

incrAndPrint :: IORef Int -> IO ()
incrAndPrint z = do
modifyIORef z (+1)
readIORef z >>= print
main = do
z <- x
incrAndPrint z
incrAndPrint z

如果你真的,真的必须使用可以使用的全局可变状态unsafePerformIO,它与GHC一起工作,因为它将确保IORef不是在初始化之前访问,并且NOINLINE阻止新的引用在你使用的任何地方创建。

import Data.IORef
import System.IO.Unsafe
x :: IORef Int
x = unsafePerformIO $ newIORef 0
{-# NOINLINE x #-}
incrAndPrint :: IO ()
incrAndPrint = do
modifyIORef x (+1)
readIORef x >>= putStrLn . show
main :: IO ()
main = incrAndPrint >> incrAndPrint

相关内容

  • 没有找到相关文章

最新更新