不要使用unsafeInterleaveIO
或懶惰IO爲這一問題。這正是迭代創建時要解決的問題:避免惰性IO,這會導致不可預知的資源管理。訣竅是永遠不會構建列表,並不斷使用iteratees對其進行流式傳輸,直到完成使用。我將使用我自己的庫中的示例pipes
來演示這一點。
首先,定義:
import Control.Monad
import Control.Monad.Trans
import Control.Pipe
-- Demand only 'n' elements
take' :: (Monad m) => Int -> Pipe a a m()
take' n = replicateM_ n $ do
a <- await
yield a
-- Print all incoming elements
printer :: (Show a) => Consumer a IO r
printer = forever $ do
a <- await
lift $ print a
現在我們是平均到我們的用戶,要求他們生產對我們來說真是大名單:
prompt100 :: Producer Int IO()
prompt100 = replicateM_ 1000 $ do
lift $ putStrLn "Enter an integer: "
n <- lift readLn
yield n
現在,讓我們來運行它:
>>> runPipe $ printer <+< take' 1 <+< prompt100
Enter an integer:
3<Enter>
3
它只提示用戶輸入一個整數,因爲我們只需要一個整數!
如果你想從getLargeList
輸出更換prompt100
,你只寫:
yourProducer :: Producer b IO()
yourProducer = do
xs <- lift getLargeList
mapM_ yield xs
...然後運行:
>>> runPipe $ printer <+< take' 1 <+< yourProducer
這會懶洋洋地流列表中,從來沒有建列表在內存中,全部不使用不安全的IO
黑客。要更改您需要的元素數量,只需更改您傳遞的值take'
有關更多示例,請參閱pipes
tutorial,地址爲Control.Pipe.Tutorial
。
要了解爲何懶IO引起的問題,閱讀關於這個問題,你可以找到here奧列格的原始幻燈片。他擅長解釋使用惰性IO的問題。任何時候你感覺不得不使用惰性IO,你真正想要的是一個迭代庫。
也許這會有所幫助。 http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –
安東,我讀過這個話題,但我沒有找到答案:是否有一個替代mapM的懶計算。 –
@DmitryBespalov不具有相同的類型簽名。 'Monad'沒有一個抽象的延遲效果,直到後來 - 這就是你必須做的,以使'mapM'變得更加懶散。 – Carl