
Hi, I am having difficulty debugging a troublesome stack overflow, which I think might be related to calling unsafePerformIO from within the IO monad. So I have the following code: import System.Random import System.IO.Unsafe import Data.Time.Clock timedIterateIO :: Int -> (a -> a) -> a -> IO a timedIterateIO durationSecs f x = do t0 <- getCurrentTime timedIterateIO' t0 x where duration' = fromIntegral durationSecs timedIterateIO' t0 x = do let y = f x t <- getCurrentTime let d = diffUTCTime t t0 if d >= duration' then return y else timedIterateIO' t0 y f x = unsafePerformIO $ do m <- randomRIO (1,2) return (m+x) The idea of the timedIterateIO function is that it should repeatedly apply f to x until a specified duration is up. The use case is for game search, where I want to keep searching until time is up, and then play the best move I've found at that point. So the following works: *Main> timedIterateIO 1 (+1) 0 45580 (1.01 secs, 360357840 bytes) The following also seems to work: *Main> unsafePerformIO $ timedIterateIO 1 f 0 67866 (1.25 secs, 394938540 bytes) Now, in the real use case, I have another function, g let's say. It is similar to f, in that it uses unsafePerformIO internally to get random numbers. It's proven to work under normal circumstances, for example: *Main> (!! 100) $ iterate g x works, and completes in around a second. However, the following doesn't work, failing with a stack overflow: *Main> unsafePerformIO $ timedIterateIO 1 g x Any ideas? Thanks, David

It looks like your timedIterateIO is too lazy. When you pass it a function like (+1) what will happen is that a large chunk of the form ...+1+1+1+1+1 is build up on your heap. When you finally need its value the large chunk will be evaluated causing it to push the '1' arguments on the stack. When there are too much '1's your stack will eventually overflow. Try evaluating the 'y' before calling timedIterateIO' again as in: let y = f x ... y `seq` timedIterateIO' t0 y I think the normal 'iterate' function also has this behaviour: iterate (+1) 0 !! 10000000 will cause your heap to be filled with lots of +1s and will finally cause a stack overflow when it tries to evaluate the final value (I haven't tried it so I may be wrong here). regards, Bas

Bas van Dijk
It looks like your timedIterateIO is too lazy.
Try evaluating the 'y' before calling timedIterateIO' again as in:
let y = f x ... y `seq` timedIterateIO' t0 y
Thank you, that appears to do the trick. I'm still a bit puzzled about why the problem only manifested in this case - it didn't manifest when I used iterate, for example.

DavidA
I am having difficulty debugging a troublesome stack overflow, which I think might be related to calling unsafePerformIO from within the IO monad.
[...]
f x = unsafePerformIO $ do m <- randomRIO (1,2) return (m+x)
As a side note you don't need unsafePerformIO here. Instead you should implement something like iterateM: iterateM :: Monad m => (a -> m a) -> a -> m [a] or change the type of your timedIterateIO to: timedIterateIO :: Int -> (a -> IO a) -> a -> IO a Also to do the actual timing you can use concurrency with SampleVar, which is cleaner and probably also faster: timedIterateIO :: Int -> (a -> IO a) -> a -> IO a timedIterateIO time f x0 = do resultVar <- newSampleVar x0 tid <- forkIO $ iterateFunc resultVar x0 threadDelay time readSampleVar resultVar <* killThread tid where iterateFunc resultVar x0 = x0 `seq` do x1 <- f x0 writeSampleVar resultVar x1 iterateFunc resultVar x1 Greets, Ertugrul -- nightmare = unsafePerformIO (getWrongWife >>= sex) http://blog.ertes.de/
participants (3)
-
Bas van Dijk
-
DavidA
-
Ertugrul Soeylemez