2012-09-26 38 views
11

想,我得到的項目大名單與IO工作時:懶惰版本MAPM的

as <- getLargeList 

現在,我想申請fn :: a -> IO bas

as <- getLargeList 
bs <- mapM fn as 

mapM有鍵入mapM :: Monad m => (a -> m b) -> [a] -> m [b],這就是我需要的類型匹配。但它會建立內存中的所有鏈,直到返回結果。我正在尋找模擬mapM,這將延遲工作,以便我可以使用bs頭,而尾巴仍在建設。

+1

也許這會有所幫助。 http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –

+0

安東,我讀過這個話題,但我沒有找到答案:是否有一個替代mapM的懶計算。 –

+0

@DmitryBespalov不具有相同的類型簽名。 'Monad'沒有一個抽象的延遲效果,直到後來 - 這就是你必須做的,以使'mapM'變得更加懶散。 – Carl

回答

18

不要使用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,你真正想要的是一個迭代庫。

+5

+100爲管庫 –

+2

Gabriel,謝謝你的回答!這是非常有用的圖書館,謝謝! –

+0

@DmitryBespalov不客氣!我總是樂於提供幫助。 –

6

的IO單子是否有一個機構推遲的影響。它被稱爲unsafeInterleaveIO。你可以用它得到想要的效果:

import System.IO.Unsafe 

lazyMapM :: (a -> IO b) -> [a] -> IO [b] 
lazyMapM f [] = return [] 
lazyMapM f (x:xs) = do y <- f x 
         ys <- unsafeInterleaveIO $ lazyMapM f xs 
         return (y:ys) 

這就是懶惰IO如何實現的。實際執行效果的順序很難預測,並且將根據評估結果列表元素的順序來確定是不安全的。由於這個原因,f中的任何IO效應都是良性的,因爲它們應該是對秩序不敏感的。一個通常效果良好的好例子是從只讀文件中讀取。

+1

從我的角度來看,在Haskell中使用System.IO.Unsafe就像是黑客攻擊。由於其「不安全」行爲,我寧願避免使用它。不過,我感謝你的回答。 –