2011-01-28 83 views
12

我試圖將Data.Binary.Put monad包裝到另一箇中,以便稍後我可以問它「將要寫入多少字節」或「文件中當前位置是什麼」等問題。但即使很瑣碎的包裝如:爲什麼包裝Data.Binary.Put monad會造成內存泄漏?

data Writer1M a = Writer1M { write :: P.PutM a } 
or 
data Writer2M a = Writer2M { write :: (a, P.Put) } 

創建一個巨大的空間泄漏和程序通常崩潰(佔用4GB的RAM後)。以下是我試過到目前爲止:

-- This works well and consumes almost no memory. 

type Writer = P.Put 

writer :: P.Put -> Writer 
writer put = put 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut writer) 

-- This one will cause memory leak. 

data Writer1M a = Writer1M { write :: P.PutM a } 

instance Monad Writer1M where 
    return a = Writer1M $ return a 
    ma >>= f = Writer1M $ (write ma) >>= \a -> write $ f a 

type WriterM = Writer1M 
type Writer = WriterM() 

writer :: P.Put -> Writer 
writer put = Writer1M $ put 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut $ write writer) 
-- This one will crash as well with exactly the 
-- same memory foot print as Writer1M 

data Writer2M a = Writer2M { write :: (a, P.Put) } 

instance Monad Writer2M where 
    return a = Writer2M $ (a, return()) 
    ma >>= f = Writer2M $ (b, p >> p') 
         where (a,p) = write ma 
           (b,p') = write $ f a 

type WriterM = Writer2M 
type Writer = WriterM() 

writer :: P.Put -> Writer 
writer put = Writer2M $ ((), put) 

writeToFile :: String -> Writer -> IO() 
writeToFile path writer = BL.writeFile path (P.runPut $ snd $ write writer) 

我是新來的Haskell,這是沒有SENCE給我,但包裝單子看起來很瑣碎,所以我猜有是我失蹤的事情。

感謝您的期待。

UPDATE: 下面是一個說明該問題的示例代碼:http://hpaste.org/43400/why_wrapping_the_databinaryp

UPDATE2: 還有一個第二部分這個問題here

+1

你使用什麼編譯器標誌? – 2011-01-28 14:36:27

+0

當你問我用-O2試過(我以前沒有用過),但記憶足跡沒有改變。 – 2011-01-28 14:49:47

回答

4

閒逛了一會兒後,我發現,這個問題似乎是二元的(>> =)來實現的使用( >>)。下面除了Writer1M單子實施解決了這個問題:

m >> k = Writer1M $ write m >> write k 

儘管這個版本還是泄漏內存:

m >> k = Writer1M $ write m >>= const (write k) 

看着binary's source,(>>)似乎丟棄第一單子的結果明確。但不知道這究竟是如何防止泄漏的。我最好的理論是,GHC否認保持PairS對象,並且「a」引用泄漏,因爲它永遠不會被查看。

2

您是否試圖讓monad更加嚴格嚴格?例如。嘗試使datatyp的構造函數嚴格/用新類型替換它們。

我不知道這裏有什麼確切的問題,但這是通常的泄漏源。

PS:還有儘量去除不必要的lambda表達式,比如:

ma >>= f = Writer1M $ (write ma) >=> write . f