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?
Just as a clarification: for an application like the one you seem to be building, creating an IORef using
unsafePerformIO
is quite normal, and allows Haskell's closest approximation to global variables in procedural languages. What is probably not wise is using it in your actions. For example, newNode is probably best written as:And then any place you intend to call
newNode
should be an IO action itself.One other little detail:
counter
will have the typeIORef Integer
by default unless you change this usingdefault
. Don't try to give it a generic type likeNum a => IORef a
: that way danger lies.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 accessnewNode
.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.Yous shouldn't use such constructs in a normal programming, as the compiler may apply various optimizations that kill the desired effect or make it unpredictable. If possible, use something like the
State
monad instead. It's cleaner and will always behave like you want. Here you go:Please see what sepp2k sad for the answer to your question. What you explain is particularly useful, if you have something global available (like configs), unlikely to change, which must be available from nearly everywhere. Use it wisely, as it is against the basic principle of purity. If it's possible, use monads instead (like I explained).