2014-05-18 36 views
0

我在Haskell中练习一些练习,探索一些我不熟悉的地方,但我一直无法理解我在混合System.TimeoutSystem.IO.Unsafe时得到的行为。超时和unsafePerformIO

我懒读一个流,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函数在程序启动时被评估过吗?我期待它,因为我立即将其部分返回值写入stdout,并且每次输入一行时,软件都会做出反应。是unsafePerformIO插入某种懒惰到我的函数?或者它是否将懒惰插入System.Timeout的内部?

+11

1)如果您不得不询问'unsafePerformIO',那么您不应该使用'unsafePerformIO'。这个例子当然是一个误用。改用'spoon'库来代替。 2)这段代码不能编译。在将来的问题中,请张贴您实际使用的代码。 –

+0

1)但是如果我不尝试在陌生的地方使用unsafePerformIO,我无法真正理解Haskell的评估规则。 2)抱歉没有编译,我完成了代码并纠正了错误。我尝试了几件不同的事情,并删除了一段帖子。 – marcosdumay

+0

另外,增加了一个强制x的评估的深度。它不会改变结果。 – marcosdumay

回答

0

我希望

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

永远不会触发下正常工作量的超时时间。上面的代码不会强制评估xy。也许使用evaluate将在这里帮助。

此外,使用unsafePerformIO这个任务看起来相当矫枉过正。应该只使用unsafePerformIO作为最后的手段。

3

原因为什么return $! (x, y)并未严格评估xy。它只评估元组构造函数,它不一定会评估它的字段xy

那么你的程序中会发生什么,return $! (x, y)立即成功,没有真正尝试评估xy。然后h ++ t部分开始评估h,这是最终开始阻止输入的时间。

顺便说一下,您不应该使用unsafePerformIO的原因:您无法轻易推断何时发生效果。

+1

你不应该使用'unsafePerformIO',因为它不是类型安全的,如果你误用它,你可以得到segfaults/etc。还有其他缺点,但这是最糟糕的部分。 –