Consider the following code that is supposed to print out random numbers:
import System.Random.Mersenne
main =
do g <- (newMTGen Nothing)
xs <- (randoms g) :: IO [Double]
mapM_ print xs
When run, I get a segmentation fault error. That is unsurprising, since the function 'randoms' produces an infinite list. Suppose I wanted to print out only the first ten values of xs. How could I do that? xs has type IO [Double], and I think I want a variable of type [IO Double]. What operators exist to convert between the two.
IO [Double] -> IO [IO Double]
... unless you unsafePerformIO
- Thomas Eding 2012-04-04 19:51
If you get a segmentation fault error, and you didn't use the FFI or any functions with unsafe
in their name, that's not unsurprising, in any situation! It means there's a bug with either GHC, or a library you're using is doing something unsafe.
Printing out an infinite list of Double
s with mapM_ print
is perfectly fine; the list will be processed incrementally and the program should run with constant memory usage. I suspect there is a bug in the System.Random.Mersenne
module you're using, or a bug the C library it's based on, or a problem with your computer (such as faulty RAM).1 Note that newMTGen
comes with this warning:
Due to the current SFMT library being vastly impure, currently only a single generator is allowed per-program. Attempts to reinitialise it will fail.
You might be better off using the provided global MTGen
instead.
That said, you can't convert IO [Double]
into [IO Double]
in that way; there's no way to know how long the resulting list would be without executing the IO
action, which is impossible, since you have a pure result (albeit one that happens to contain IO
actions). For infinite lists, you could write:
desequence :: IO [a] -> [IO a]
desequence = desequence' 0
where
desequence n m = fmap (!! n) m : desequence (n+1) m
But every time you execute an action in this list, the IO [a]
action would be executed again; it'd just discard the rest of the list.
The reason randoms
can work and return an infinite list of random numbers is because it uses lazy IO with unsafeInterleaveIO
. (Note that, despite the "unsafe" in the name, this one can't cause segfaults, so something else is afoot.)
1 Other, less likely possibilities include a miscompilation of the C library, or a bug in GHC.
IO [Double]
into [IO Double]
... there's no way to know how long the resulting list would be without executing the IO action - Dan Burton 2012-04-04 20:56
take
on the resulting list: mapM_ print (take 10 xs)
- ehird 2012-04-04 22:00
Suppose I wanted to print out only the first ten values of xs. How could I do that?
Just use take
:
main =
do g <- (newMTGen Nothing)
xs <- (randoms g) :: IO [Double]
mapM_ print $ take 10 xs
You wrote
xs has type IO [Double]
But actually, randoms g
has type IO [Double]
, but thanks to the do
notation, xs
has type [Double]
, you can just apply take 10
to it.
You could also skip the binding using liftM
:
main =
do g <- newMTGen Nothing
ys <- liftM (take 10) $ randoms g :: IO [Double]
mapM_ print ys