How to best "waste" a roughly specified time by only "burning CPU" with pure functional calculations?

StackOverflow https://stackoverflow.com/questions/22282337

  •  11-06-2023
  •  | 
  •  

Question

I occasionally would like to delay specific parts of a pure algorithm while developing / testing, so I can monitor the evaluation simply by watching the lazy result build up piece by piece (which would generally be too fast to be useful in the final, un-delayed version). I then find myself inserting ugly stuff like sum [1..1000000] `seq` q, which kind of works (though often with the usual thunk-explosion problems, because I never think much about this), but is rather trial-and-error-like.

Is there a nicer, more controllable alternative that's still just as simple, when I want to do some quick testing in that way and can't be bothered to do proper profiling, criterion etc.?

I'd also like to avoid unsafePerformIO $ threadDelay, though I reckon this might actually be an appropriate use.

Was it helpful?

Solution

This looping solution avoids calling threadDelay, but still calls unsafePerformIO, so maybe we don't gain much:

import Data.AdditiveGroup
import Data.Thyme.Clock
import Data.Thyme.Clock.POSIX
import System.IO.Unsafe

pureWait :: NominalDiffTime -> ()
pureWait time = let tsList = map unsafePerformIO ( repeat getPOSIXTime ) in
    case tsList of
        (t:ts) -> loop t ts
    where
        loop t (t':ts') = if (t' ^-^ t) > time
            then ()
            else loop t ts'

main :: IO ()
main = do
    putStrLn . show $ pureWait (fromSeconds 10)

UPDATE: Here's an altenative solution. First determine (using IO) how many iterations do you need to achieve a given delay, and then just use a pure looping function.

pureWait :: Integer -> Integer
pureWait i = foldl' (+) 0 $ genericTake i $ intersperse (negate 1) (repeat 1)

calibrate :: NominalDiffTime -> IO Integer
calibrate timeSpan = let iterations = iterate (*2) 2 in loop iterations
    where
    loop (i:is) = do
        t1 <- getPOSIXTime
        if pureWait i == 0
            then do
                t2 <- getPOSIXTime
                if (t2 ^-^ t1) > timeSpan
                   then return i
                   else loop is
            else error "should never happen"

main :: IO ()
main = do
    requiredIterations <- calibrate (fromSeconds 10)
    putStrLn $ "iterations required for delay: " ++ show requiredIterations
    putStrLn . show $ pureWait requiredIterations
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top