为了便于理解,我尝试了 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)