为了便于理解,我尝试了 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
有人可以解释为什么吗?此外,有没有办法让这个“工作”?如果没有,为什么?
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
如果你真的、真的必须使用全局可变状态,你可以使用 来实现
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