0

我在 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

使用这样的过滤器,程序会按预期读取所有标准输入,并输出到标准输出。但如果我这样做:

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在程序启动时评估该功能是错误的吗?我希望它是,因为我立即将它的部分返回值写入标准输出,并且每次我输入一行时软件都会做出反应。是否unsafePerformIO在我的函数中插入了某种惰性?还是将惰性插入到内部System.Timeout

4

2 回答 2

3

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

因此,您的程序中发生的事情是return $! (x, y)立即成功,而无需实际尝试评估xand y。然后该h ++ t部分开始评估h它最终开始阻塞输入的时间。

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

于 2014-05-18T21:29:27.807 回答
0

我希望

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

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

此外,unsafePerformIO用于此任务看起来相当过分。使用unsafePerformIO应该仅作为最后的手段。

于 2014-05-18T18:59:38.260 回答