【问题标题】:Haskell -- Forcing strict evaluation with a weird, recursive typeHaskell——用一种奇怪的递归类型强制进行严格的评估
【发布时间】:2014-09-16 18:19:18
【问题描述】:

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)

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

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

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

【问题讨论】:

    标签: haskell timeout lazy-evaluation


    【解决方案1】:

    看来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
    

    【讨论】:

    • OP 已经在使用($!) 并且不能使用($!!),除非他们提供NFData A 实例。
    • 谢谢!我可能会多次重新阅读这个答案,直到它真正点击为止。对于那些跟随的人,使用它的正确方法是testTimeout x = timeout 1000 . evalM . evalRandIO $ runA x x 0
    • 啊,但是(1)他没有像我那样使用$!,(2)他没有NFData m Bool,但我使用它的方式只需要NFData Bool .
    • @user3873438 请记住,在 Haskell 中,当您执行一个操作时,它可能不会评估其结果,并将其留给下一个人。懒惰的评估无处不在,超时并不会影响这一点。
    【解决方案2】:

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

    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
    

    这甚至可能“过于严格”并且过于严格,不确定。

    【讨论】:

      猜你喜欢
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 2013-11-16
      • 2011-07-04
      相关资源
      最近更新 更多