2011-01-28 84 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