haskellmonadsiorefunsafe-perform-io

Confusion over IORefs to make a counter


I found some sample code, and changed it a little

counter = unsafePerform $ newIORef 0

newNode _ = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i

Which returns 1 then 2 then 3 then 3 etc each time it's run.

But when I change it to

newNode = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i

then I get 0 every time I run it.

Why is this happening, and what can I do to fix it?


Solution

  • In your second version newNode is a simple value, not a function. So haskell evaluates it exactly once and then gives you the result of that evaluation whenever you access newNode.

    A word of warning: Using unsafePerformIO on anything other than an IO action which you know to be referentially transparent is dangerous. It might interact badly with some optimizations and just generally not behave like you expect. There's a reason it's got the word "unsafe" in its name.

    As a way to play around with unsafePerformIO your code is fine, but if you ever want to use something like it in real code, I'd strongly encourage you to reconsider.