Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

haskell - Why wrapping the Data.Binary.Put monad creates a memory leak?

I'm trying to wrap the Data.Binary.Put monad into another so that later I can ask it questions like "how many bytes it's going to write" or "what is the current position in file". But even very trivial wraps like:

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

create a huge space leak and the program usually crashes (after taking up 4GB of RAM). Here is what I've tried so far:

-- 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)

I'm new to Haskell and this makes no sence to me, but the wrapper monads seem very trivial so I'm guessing there is something obvious I'm missing.

Thanks for looking.

UPDATE: Here is a sample code that demonstrates the problem: http://hpaste.org/43400/why_wrapping_the_databinaryp

UPDATE2: There is also a second part to this question here.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After poking around for a bit, I found that the problem seems to be the usage of binary's (>>=) to implement (>>). The following addition to the Writer1M monad implementation solves the problem:

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

Whereas this version still leaks memory:

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

Looking at binary's source, (>>) seems to discard the result of the first monad explicitly. Not sure how exactly this prevents the leak, though. My best theory is that GHC otherwise holds onto the PairS object, and the "a" reference leaks because it never gets looked at.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...