4

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?

Robin Green
  • 32,079
  • 16
  • 104
  • 187
Squidly
  • 2,707
  • 19
  • 43

3 Answers3

10

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.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Why is it not treated a nonary function? – Squidly Dec 16 '10 at 15:10
  • 6
    @MrBones: Because that behavior would almost never be what people want. If I write `x = veryExpensiveFunction foobar` and then later `y = x * x + x`, I want `veryExpensiveFunction foobar` to be evaluated once, not thrice. And that's exactly what Haskell does (unless `x`'s type is polymorphic). The only reason you want it to behave like a nullary function in this case is that evaluating the expression has side-effects, which isn't something that can even happen without unsafe operations. – sepp2k Dec 16 '10 at 15:19
  • 1
    There is no such thing as a nonary function in haskell. Functions all take one value (which may be a tuple) and return one value (which may be another function). Functions are not "invoked" -- they are applied to values, to yield values. Do you seriously want `foo = 1 + 2` to be the function which tells the processor to add 1 and two every time you use the word foo!? – sclv Dec 16 '10 at 15:22
  • Aha. I forgot about functions all being of a -> b, so I guess nonary can't possibly work with that, and what appears nonary isn't. Cheers – Squidly Dec 16 '10 at 15:55
  • 3
    Independently of whether a value can be considered a 0-argument function (which, IMO, loosely it can - just like 'x -> y -> z' can be thought of loosely as the type of binary functions), purity means that such a function must always return the same value. It is always called with the same set of arguments (none), and therefore must always return the same value or it is by definition not pure and to use 'unsafePerformIO' to assert that it is would be incorrect. – mokus Dec 16 '10 at 16:08
  • 2
    Currently (GHC 7.6.3) `newNode ()` is sure to interact badly with optimizations: when compiled with `-O` it only runs once! E.g., `newNode () + newNode ()` evaluates to `0`. – ntc2 Oct 04 '13 at 01:01
4

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:

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

And then any place you intend to call newNode should be an IO action itself.

One other little detail: counter will have the type IORef Integer by default unless you change this using default. Don't try to give it a generic type like Num a => IORef a: that way danger lies.

Dan
  • 10,990
  • 7
  • 51
  • 80
  • 1
    Just out of curiosity, what are the dangers you speak of with using `Num a => IORef a`? – David Powell Dec 17 '10 at 02:03
  • Because the typesystem gets screwed upo with it. I read somewhere about this, but can't find the ressource anymore. Probably in the sourcecode for `unsafePerformIO` – fuz Dec 17 '10 at 04:21
  • 1
    @Grazer: It can violate type safety; see [the documentation for `unsafePerformIO`](http://hackage.haskell.org/packages/archive/base/latest/doc/html/System-IO-Unsafe.html#v:unsafePerformIO). (Although their example now prints `"*"` for me, instead of dumping core, that still shouldn't be possible.) You can also produce a value which changes the second time you evaluate it. – Antal Spector-Zabusky Dec 17 '10 at 04:29
3

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:

-- This function lets you escape from the monad, it wraps the computation into a monad to make it, well, countable
counter :: (State Int x) -> x
counter = flip evalState 0

-- Increments the counter and returns the new counter
newNode :: State Int Int
newNode = modify (+1) >>= get

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).

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 8
    To underline the answers here -- the best way to fix it is to not ever ever ever ever *ever* **ever** use `unsafePerformIO` unless you *really* really *really* **really** r-r-r-e-e-e-a-a-a-l-l-l-y know what you're doing and why you need it. And even then, probably don't use it. – sclv Dec 16 '10 at 15:23