Haskell - 使用奇怪的递归类型强制进行严格评估

时间:2014-07-25 01:10:57

标签: haskell timeout lazy-evaluation

previously问了一个关于如何强制严格评估以创建超时的问题。大多数情况下使用seq / $!就足够了,deepseq适用于NFData成员的所有内容,但如果我们使用奇怪的递归类型会怎么样?假设我们有以下内容:

import Control.DeepSeq
import Control.Monad.Random
import Data.Maybe
import System.Timeout

newtype A = A { runA :: A -> Int -> Rand StdGen Bool }

-- a call to runA with this as the first input will always terminate
example1 :: A
example1 = A (\_ i -> (if i > 0 then getRandomR (True, False) else return False))

-- a call to runA with this as the first input will never terminate
example2 :: A
example2 = A (\_ _ -> runA example2 example2 0)

-- here it depends on the other input 
-- will terminate with example1, not with example2 or 3
example3 :: A
example3 = A (\a _ -> runA a a 0)

我们是否可以编写一个超时函数来确定当我们调用x时,类型A的某个值runA x x 0是否会在给定的时间内终止?我们可以尝试使用seq,如下所示:

testTimeout :: A -> IO (Maybe Bool)
testTimeout x = timeout 1000 . evalRandIO $! runA x x 0

但是,这对example2example3不起作用,因为对runA的调用会被评估为WHNF,但随后会挂起,因为计算永远不会完成。使用deepseq(即$!!)尝试相同的操作甚至无法编译,因为NFData需要一个Rand StdGen Bool实例。那么,我们如何实现这个实例,以便严格的评估/超时按预期工作?或者还有其他方法可以做到这一点吗?

2 个答案:

答案 0 :(得分:2)

timeout似乎只是在一定时间内执行操作而不评估结果。它不评估产生的内部。好的,可以。如果我们使用

(>>= (return $!)) :: Monad m => m a -> m a

如您所知,return会创建m a类型的值。通过执行return $!,我们说我们不会生成m a,因此完成操作,直到评估结果。这是一个更详细的功能。

evalM m = do
    result <- m
    result `seq` return result

您也可以使用NFData执行此操作(Bool不是必需的,但如果您使用的是[a],则可以执行此操作):

(>>= (return $!!)) :: (Monad m, NFData a) => m a -> m a

更详细地说:

forceM m = do
    result <- m
    result `deepseq` return result

答案 1 :(得分:1)

咦。那是一个奇怪的小型。也许这个?

instance NFData A where
    rnf (A !runA) = ()

strictify :: A -> A
strictify (A !runA) = A $ \a i -> strictify a `deepSeq` i `deepSeq` runA a i

testTimeout x = timeout 1000 . evalRandIO $! runA x' x' 0
 where x' = strictify x

甚至可能过于严格&#34; 冗余严格,不确定。