Risks of using unsafeperformIO on randomIO
Asked Answered
G

2

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?

Greenlaw answered 25/4, 2013 at 6:54 Comment(7)
You should use random or randomR in pure code.Callboard
NO! You might be able to justify unsafeInterleaveIO but nothing random is pure!Sabrina
@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.Heim
@leftaroundabout: Good example!Elery
@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.Builtin
I question the need to use unsafePerformIO at all. Why do you say that you must only use pure functions?Erelong
I'd suggest you to use the Rand monad or its 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.Rappee
E
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).

Elery answered 25/4, 2013 at 8:50 Comment(2)
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.Supplicant
I had that as my example first, but I could not tickle GHC enough to make that actually observable, therefore this example.Elery
L
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.

Lawerencelawes answered 13/4, 2018 at 23:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.