For the sake of understanding I played around with IORef and tried to come up with something that would be close to having a global, mutable state:
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
However, to my surprise this prints
1
1
not
1
2
Could someone explain why? Moreover, is there a way to make this 'work'? If not, why?
CodePudding user response:
Your x
is an IO
action that creates a new IORef Int
so when you use it it will always create a new one starting at 0
.
You can easily make this work by increment the same reference both times:
incrAndPrint :: IORef Int -> IO ()
incrAndPrint z = do
modifyIORef z ( 1)
readIORef z >>= print
main = do
z <- x
incrAndPrint z
incrAndPrint z
If you really, really have to use a global mutable state you can do it with
unsafePerformIO
, which works with GHC because it will make sure the IORef
isn't
accessed before it's initialized and the NOINLINE
prevents a new reference to be
created wherever you use it.
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