I want to evaluate random computations in Haskell with a timeout using the Control.Monad.Random
library. The following works just fine:
ghci> import System.Timeout
ghci> import Control.Monad.Random
ghci> timeout 1000 . evalRandIO $ getRandomR (True, False)
Just True
However, this approach does not seem to work if we have a computation of type Rand StdGen a
that never terminates (i.e. evaluates to bottom). For example:
ghci> let f = f :: Rand StdGen Bool
ghci> timeout 1000 $ evalRandIO f
Just
Here GHCI prints "Just " and then hangs indefinitely trying to evaluate f
. Can someone who knows more about the Haskell runtime than I do explain why this happens, and how to get around it? My guess is that the expression evalRandIO f
is evaluated to WHNF and so timeout 10
thinks the computation is going to terminate, but I really have no idea.
This might make more sense if you were to do something like
> Just x <- timeout 1000 $ evalRandIO f
> :t x
x :: Bool
> x
Interrupted.
The computation itself is completing, namely it's reaching WHNF, so timeout
does not catch it. The timeout 1000
function itself completes and returns Just undefined
. An example where you can get timeout
to catch a bottom evaluation would be
> import Control.DeepSeq
> :set +m -- Multiline mode
> let f :: Bool
| f = f
|
> timeout 1000000 $ deepseq f (return f)
Nothing
You'll see that it hangs for a second, then returns Nothing
when deepseq
does not finish evaluating f
so it can perform return f
.
So yes, your problem is stemming from the fact that f = f
gets evaluated to WHNF instead of NF. In order to force NF, you need to use something like deepseq
. Another possibly simpler example would be to just use the $!
operator, such as:
> let f :: Rand StdGen Bool
| f = f
|
> timeout 1000000 $ evalRandIO $! f
Nothing
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With