Timeout和unsafePerformIO

时间:2014-05-18 18:34:20

标签: haskell timeout lazy-evaluation unsafe-perform-io

我正在Haskell做一些练习,探索一些我不熟悉的领域,但我无法理解混合System.TimeoutSystem.IO.Unsafe时的行为。

我懒惰地用getContents读取一个流,用纯函数过滤它,然后输出结果。典型的过滤器就像:

import Data.List(break)
import System.Timeout(timeout)
import System.IO.Unsafe(unsafePerformIO)

main = do
    text <- getContents 
    putStr $ aFilter text
aFilter text = h ++ t where
    (h, t) = getUntil "\n" text
getUntil separator text = break (\x -> (separator!!0) == x) text

使用这样的过滤器,程序按预期读取所有stdin,并输出到stdout。但如果我做了类似的事情:

aFilter text = result where
    l = consumeWithTimeout (getUntil "\n" text)
    result = case l of
        Nothing -> "Timeout!\n"
        Just (h, t) -> h ++ t

consumeWithTimeout (x, y) = unsafePerformIO $! timeout 6 $! deepseq x (return (x, y))

我希望我的程序立即超时,打印“Timeout!”消息,并关闭。相反,它挂在那里,等待输入。

我认为在程序启动时评估timeout函数是错误的吗?我希望它是,因为我立即将其部分返回值写入stdout,并且每次输入一行时软件都会做出反应。 unsafePerformIO是否在我的函数中插入了某种懒惰?或者它是否将惰性插入System.Timeout的内部?

2 个答案:

答案 0 :(得分:3)

原因是return $! (x, y)没有严格评估xy。它只评估元组构造函数,它不一定会评估其字段xy

因此,您的计划中发生的事情是return $! (x, y)会立即成功,而不会真正尝试评估xyh ++ t部分然后开始评估h,这是它最终开始阻止输入的时间。

顺便说一句,这是你不应该使用unsafePerformIO的原因:你不能轻易推断实际发生效果的时间。

答案 1 :(得分:0)

我希望

timeout 6 $! return $! (x, y)

永远不会在正常工作负载下触发超时。上述代码不会强制评估xy。也许使用evaluate会有所帮助。

此外,使用unsafePerformIO执行此任务看起来相当矫枉过正。使用unsafePerformIO只能作为最后的手段。