2014-05-18 29 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。還有其他缺點,但這是最糟糕的部分。 –