6

I am creating a Haskell application that generates a random number on an infinite loop (only when requested by a client). However, I should only use pure functions for that purpose. Is it safe to wrap randomIO with unsafeperformIO without any drastic stability or performance risk?

Jk1
  • 11,233
  • 9
  • 54
  • 64
Axel Advento
  • 2,995
  • 3
  • 24
  • 32
  • 6
    You should use [`random`](http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:random) or [`randomR`](http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:randomR) in pure code. – Mikhail Glushenkov Apr 25 '13 at 07:55
  • 1
    NO! You might be able to justify `unsafeInterleaveIO` but nothing random is pure! – Philip JF Apr 25 '13 at 08:05
  • 7
    @PhilipJF: "nothing random is pure"? I'd say things like (not–in-place) quicksort with random pivot are pure algorithms allright: the randomness can't be observed from the outside except through performance variations, which only have any meaning in the `IO` monad anyway. – leftaroundabout Apr 25 '13 at 09:20
  • 1
    @leftaroundabout: Good example! – Joachim Breitner Apr 25 '13 at 14:42
  • 4
    @leftaroundabout: Well, that quicksort itself isn't random then, is it? It's as pure as `runST`, there's just no easy way to enforce it the way `ST` does. The random values are still "locally" impure, though. – C. A. McCann Apr 25 '13 at 16:29
  • 3
    I question the need to use `unsafePerformIO` at all. Why do you say that you must only use pure functions? – Gabriella Gonzalez Apr 25 '13 at 18:24
  • I'd suggest you to use the `Rand` monad or its [`RandT`](http://hackage.haskell.org/package/MonadRandom-0.1.3/docs/Control-Monad-Random.html#t:RandT) transformer. This way you can make your code pure, without needing to use `IO` or to carry the random state around all the time. – Petr Jul 07 '14 at 18:37

2 Answers2

13

Any use of unsafePerformIO should be justified by a proof that the resulting value is still pure. The rigour of the proof is up to you and the importance of the work. For example, this pathetic use unsafePerformIO and randomIO should be safe, because you can prove that when slowTrue returns anything, it will return True.

import System.Random
import System.IO.Unsafe
import Data.Int

slowTrue = unsafePerformIO $ go
  where
    go = do
        x1 <- randomIO
        x2 <- randomIO
        if ((x1 :: Int16) == x2) then return True else go

The following tempting definition of a global, possibly random variables is not safe:

rand :: Bool -> Int
rand True = unsafePerformIO randomIO 
rand False = 0

The problem is that the same expression will now yield different values:

main = do
    print (rand True)
    print (rand True)

prints here:

-7203223557365007318
-7726744474749938542

(at least when compiled without optimizations – but that just stresses the fragility of inappropriate use of unsafePerformIO).

Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139
  • That's an interesting question. Top level definition `unknown = unsafePerformIO randomIO` is actually safe if compiler will evaluate body of `unknown` only once. But I'm pretty sure that compiler have rights to inline it and/or calculate many times. – ony Apr 25 '13 at 12:24
  • 2
    I had that as my example first, but I could not tickle GHC enough to make that actually observable, therefore this example. – Joachim Breitner Apr 25 '13 at 14:41
0

I am creating a Haskell application that generates a random number on an infinite loop (only when requested by a client). However, I should only use pure functions for that purpose. Is it safe to wrap randomIO with unsafeperformIO without any drastic stability or performance risk?

Aside from performance (no risk) or stability (small risk), consider also the fact that you are writing non-canonical Haskell for no good reason. Your code will be difficult for anyone else to maintain if you take this approach.