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

Soe*_*ren 6 haskell

为了便于理解,我尝试了 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
Run Code Online (Sandbox Code Playgroud)

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

1
1
Run Code Online (Sandbox Code Playgroud)

不是

1
2
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么吗?此外,有没有办法让这个“工作”?如果没有,为什么?

caf*_*e25 10

Yourx是一个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
Run Code Online (Sandbox Code Playgroud)

如果你真的、真的必须使用全局可变状态,你可以使用 来实现 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
Run Code Online (Sandbox Code Playgroud)