我想使用Control.Monad.Random
库评估Haskell中的随机计算超时。下面的代码可以正常工作:
ghci> import System.Timeout
ghci> import Control.Monad.Random
ghci> timeout 1000 . evalRandIO $ getRandomR (True, False)
Just True
然而,如果我们有一个类型为Rand StdGen a
的计算永远不会终止(即求值到底部),这种方法似乎不起作用。例如:
ghci> let f = f :: Rand StdGen Bool
ghci> timeout 1000 $ evalRandIO f
Just
这里GHCI打印"Just",然后无限期挂起,试图评估f
。谁能比我更了解Haskell运行时解释为什么会发生这种情况,以及如何绕过它?我的猜测是,表达式evalRandIO f
被评估为WHNF,因此timeout 10
认为计算将终止,但我真的不知道。
如果您要做像
这样的事情,这可能更有意义> Just x <- timeout 1000 $ evalRandIO f
> :t x
x :: Bool
> x
Interrupted.
计算本身正在完成,即它达到了WHNF,所以timeout
没有捕获它。timeout 1000
函数本身完成并返回Just undefined
。可以让timeout
捕获底部求值的示例是
> import Control.DeepSeq
> :set +m -- Multiline mode
> let f :: Bool
| f = f
|
> timeout 1000000 $ deepseq f (return f)
Nothing
您将看到它挂起一秒钟,然后当deepseq
没有完成f
的计算时返回Nothing
,因此它可以执行return f
。
所以,是的,你的问题是源于f = f
被评估为WHNF而不是NF。为了强制NF,您需要使用deepseq
之类的东西。另一个可能更简单的例子是只使用$!
操作符,例如:
> let f :: Rand StdGen Bool
| f = f
|
> timeout 1000000 $ evalRandIO $! f
Nothing