Haskell monad: IO [Double] to [IO Double]

Go To StackoverFlow.com

9

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.

2012-04-04 19:25
by Gautam
Incidentally, IO [Double] -> [IO Double] is essentially the reverse type signature of 'sequence' - Gautam 2012-04-04 19:27
It doesn't segfault here - Daniel Wagner 2012-04-04 19:42
Sounds like a miscompilation of some kind or a hardware issue, then... you might want to run a memtest86+ check - ehird 2012-04-04 19:44
You could do a IO [Double] -> IO [IO Double] ... unless you unsafePerformIO - Thomas Eding 2012-04-04 19:51
That stray -} is quite unusual - Dan Burton 2012-04-04 20:56
Daniel, does the code work properly for you? and the } is a typ - Gautam 2012-04-04 21:50


11

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

2012-04-04 19:30
by ehird
Just for the record, I think it's possible something is wrong with the questioner's computer, not with the library; the supplied code doesn't segfault for me - Daniel Wagner 2012-04-04 19:43
+1 for "you can't convert 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
So there is no way to access just the first ten list elements - Gautam 2012-04-04 21:51
@Gautam: Sure there is, just use take on the resulting list: mapM_ print (take 10 xs) - ehird 2012-04-04 22:00
@DanielWagner, you are correct, when I recompiled the mersenne-random package without using the -f use sse2 flag, it worked without a segfault. That is slightly worrying, since the -f use sse2 flag is supposed to be set so that the module can take advantage of SIMD optimization - Gautam 2012-04-05 01:08
>
  • 1, @ehird, your suggestion worked. Thanks!
  • - Gautam 2012-04-05 01:09
    You're welcome! By the way, it could also be a bug in the SSE2-specific C code that the library contains - ehird 2012-04-05 01:12
    this doesn't answer a direct question (about 10 values), and fails to correct the OP's basic misunderstanding with the type, instead discussing a misformulated question as if it had merit here. (-1 - Will Ness 2012-04-08 06:43


    11

    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
    
    2012-04-05 02:43
    by Norman Ramsey
    Ads