为了便于理解,我摆弄了一下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